Skip to content

Jobs, bringing media in, the sync and renders

Work that outlives the page that asked for it, the one way media enters a film, and comparing its local files with a sharing target.

6.12 Application-owned jobs — filmopen_jobs

Section titled “6.12 Application-owned jobs — filmopen_jobs”

The engine and its contracts: filmopen_jobs is the pure-Dart package for application-owned jobs, independent of the film and its screens, with no runtime dependency and no app import. Its public barrel exports job_api.dart (hiding copyJobData), the JobEngine factory and interface in job_engine.dart, and producer.dart, plus only refuseCredentialKeys from job_record.dart. The API includes JobView, actions, requests, checkpoints, runner results and JobRun; the scheduler, records, journal, 30-second lease, storage and execution claims remain internal and are never exported. The app reaches execution through insertion and the state adapter; its local attention composition uses the already declared jobs edge without changing the shared package graph. State constructs and owns the application service; forms import only the public job views and actions they display.

Insertion owns a scoped view projection over each exact loaded record’s id, kind and opaque payload. The generic engine supplies those existing values without interpreting producer recovery or render policy. The projection changes only state arguments; every other engine view field retains its ordinary meaning. Its private Zone door, Symbol('filmopen.insertion.viewProjection'), has type Map<String, String> Function(String, String, Map<String, Object?>, Map<String, String>): the exact record id, kind, opaque payload and a copy of the state arguments. A present value of the wrong type leaves the ordinary unprojected view in place and reports jobs.projectionIgnored once per engine, without carrying the rejected value.

JobView equality covers all eleven presentation fields. State-argument maps compare by entries and action sets by membership, independently of insertion order; their hashes follow the same rule. The state adapter filters each snapshot by place and applies distinct(listEquals), preserving job order while suppressing repeated equal snapshots, including repetitions of the initial seed and empty results. A changed field or order still publishes.

The engine factory is inert: construction performs no I/O, and explicit submit or restore activates work. Restoring one project leaves other projects dormant; the caller reconstructs project-specific bindings, which stay in memory. The engine retains BindJob and may invoke it after restore returns, when a later journal reload or lease takeover discovers work; each invocation must recheck its binding context. Explicit restore keeps journal observation active even when its initial load is empty, allowing later work to be discovered; session-only submission alone still performs no disk access. An explicit restore that meets an active attempt preserves that attempt’s binding until its claim is released, then discards the old cached binding before another attempt starts. The next attempt reconstructs its binding through the latest retained binder. A binder that throws at attempt admission fails only that record: it waits as bindingFailed, with a safe error code and a one-second saved retry deadline. Its claim is released, no runner effect occurs, and eligible siblings continue in the same tick. Once Stop is accepted, a failed cleanup binding remains stopping with no second Stop action; it keeps its saved one-second retry deadline and pending cancellation intent until cleanup can reconstruct its binding. It is reported as attemptFailed with the exception’s type and its whole message, as every report of the engine is. The caller selects session or durable persistence: session jobs touch no disk, even when a directory is configured, while each durable job has one JSON file. Clocks and storage are injected for tests. Credentials never belong in a payload; an insertion’s payload keeps its names, file paths and URLs as they are (§6.13), and presentation names and preview paths are not persisted. A result can name the file it is about: JobResult.done and JobResult.needsAttention take a displayName, null by default, which the engine puts in the job’s view when the attempt ends.

refuseCredentialKeys(Map) recursively checks payload maps and lists once, at each door a payload enters a record by: a submission, a checkpoint that carries a payload, and a record read from the file journal (kept out and reported as unreadableRecord); the engine reports a refused submission or checkpoint as unsafePayload and the caller receives the refusal. A record’s own construction, change or copy does not walk the payload again, so a checkpoint without a payload or a phase change costs no walk. A refusal names the key it refused (JobUnavailable(unsafePayload: key password)), never its value. Insertion’s and the sync’s own checks of the payloads they make stay where they are. Key names are split into words across case, separators and digits; neighbouring word runs matching known credential names are refused. A key, keys, token or tokens tail, including trailing qualifiers such as value or header, is refused when its value contains nonempty text directly or inside a map or list. Refusal is JobUnavailable('unsafePayload'). Thus textual apiKey, openai_token, Token, accessTokens and payload projectKey are refused, while tokenCount: 3, submission and project are allowed. The rule does not inspect the record’s own projectKey or placeKey, nor the lease’s fencing token. The helper is the only exported declaration from the otherwise private record file.

The scheduler starts eligible jobs in seq order within each lane’s capacity, with job and lane holds carrying their reason and actions. An exact capacity entry overrides prefix entries; otherwise the longest key ending in * whose preceding text matches the lane supplies its capacity. A lane without a matching entry has capacity one. A successful queued submission commit accepts the job and retains its runtime binding even if the following scheduling tick fails; submit returns that same job id and reports the scheduling failure, so a later tick can continue the accepted job. A diagnostic observer that throws cannot change that acceptance. Job and lane holds compose independently, and a due retry exposes its effective lane hold. Retry deadlines survive restart. A new lease generation reloads persisted records before scheduling or assigning a sequence, including when this engine owned an earlier lease. A stop or a lane hold is signalled to the active runner only after its intent is successfully persisted, durably in the journal for a durable job; a refused commit does not interrupt work. Acknowledgement never waits for the runner, external cancellation or lane capacity. Stored stop and hold intentions are honored before a recovered runner starts. There is no pause action: a person stops a job, and a hold comes from its request or its lane’s policy. Registration declares interruptible, false by default. Holding a lane interrupts only active attempts of a kind registered as interruptible. If the kind has a cancellation callback, successful cleanup queues the job again with its checkpoints intact, no job hold of its own, and the lane policy’s reason and actions. Without a cancellation callback, only a runner result of cancelled requeues the job under the hold; done, failed, needsAttention and retry retain their result. The consumed hold intent is cleared, preserving the retry deadline and the effective lane hold’s actions. Releasing the lane permits a new attempt. An immediately released hold still interrupts the current attempt, and another lane is unaffected. A user stop wins over the hold, including during cleanup. Failed cleanup remains actionable and retry preserves the interruption’s intent, so a hold retry cleans up then requeues instead of cancelling the job. A stale attempt’s lease token fences its writes, and a nonblocking execution claim per job prevents a new attempt from overlapping an old effect that is still running, without occupying another job’s lane slot. A job whose runner is unknown stays saved and needs attention, with retry/dismiss so a later build can execute it. Completed jobs expire after a day or are removed by dismissal. The expiry is the engine’s own decision, and it reports it through the same callback as its failures: expire as it decides, with the job’s kind and phase, when it finished (doneAt), its age, the rule (doneForADay) and the journal it leaves (durable, the job’s file, or session); expired as the removal ends, with the same values, the outcome (removed, or failed with the error’s type and text) and the milliseconds; a refused removal still fails the tick, and the record stays until a later tick removes it.

register(kind, runner, cancel:..., interruptible: false) accepts an optional cancellation callback and allows a kind to opt into lane-hold interruption. The running attempt retains the policy captured when it starts. A stopped or lane-held job that previously started runs that cleanup under its execution claim before becoming cancelled or requeued respectively, including when a persisted interruption is recovered. After successful intent persistence, a queued job that never started cancels without invoking either callback. Cleanup effects still persist intent and obey the lease, but do not wait for lane capacity or lane holds; a busy execution claim leaves cleanup pending. A cleanup refusal or exception leaves the job needing attention. Retry preserves its stop or hold intent and runs cleanup alone before deciding whether to cancel or requeue, never the ordinary runner during that cleanup. A cleanup that answers a held waiting — a waiting result without a retry delay — has released its stop: the producer still holds the work, and the job waits with that result’s code and actions, its stop intent cleared and no cleanup pending, so its Retry runs the ordinary runner, and a Stop after that runs the cleanup again. A delayed retry from a cleanup is not held: it needs attention with its intent, as a refusal does. A cleanup’s result, once committed, is reported as cleanupResult — except a lane hold’s requeue, which keeps its own path: the kind, the phase and state code before and after, what the cleanup answered (its phase, code and retry delay), the intent, the intent and pending flag left, the actions, the decision (held, cancelled or attention) and its reason.

Uploads register as interruptible: an interrupted import takes back what it copied, and the job imports anew once the hold is released. Producers do not: a producer is never interrupted by a hold, so an active producer under a hold continues working and attaches its output normally. ProducerRun.cancel() answers whether the producer is over: true where it is over, false where the same work is still held and can be followed again, with fresh subscriptions and a repeatable cancellation; a refusal is a throw. The render’s run reads the answer from the manager’s state after its stop, so a stop left unconfirmed, or refused while the render finishes, answers false. The held lane blocks subsequent jobs, including producers, until release. A policy hold never restarts paid work.

Producer and ProducerRun are contracts used by deterministic stand-ins and the application’s managed render producer. Producers open their own byte streams through ProducedItem.open; consumers receive no fetch URL. The consumer starts or restores a run, observes its state, invokes cancellation only through committed cleanup, and acknowledges after keeping its output.

The lease is renewed only after one third of its lifetime has passed since its last stamp (10 seconds by default); acquisition and a guarded effect still stamp immediately. Per-job claim files are swept after journal loading and terminal completion, retaining nonterminal jobs and any claim currently held. Claim acquisition and sweep share the folder’s permanent claims.lock, with a local queue for separate instances and an OS lock for separate processes. No process can open a claim between the sweep’s close and deletion. A locally held inode is not reopened even to probe it, because closing another descriptor can drop a POSIX lock. Cleanup failures report lockNotRemoved without preventing scheduling; the permanent coordination inode remains. An attempt retains whether it is durable until its claim is released, so dismissing its terminal record while release drains still triggers the final lock sweep.

The journal’s internal JournalWrites seam (journal_io.dart) makes write failures testable; its production implementation, DiskJournalWrites, flushes a temporary file in the destination’s directory before renaming it into place. Six fault tests cover a partial temporary write or flush failure, a rename failure and a failure after rename, each for an initial write and a replacement. A replacement leaves the old or new complete record, never a partial record; an initial write leaves no record before rename, or the complete new record after it. Unsupported schemas and malformed records are preserved on disk and reported through the factory’s optional onJournalProblem(id, code, [error]) callback, rather than loaded as jobs, at every load that keeps it out: one restore loads twice, and a window without the lease loads every second, each load reporting it again. The callback also reaches the engine for session and durable jobs: unexpected runner failures report attemptFailed, scheduled tick failures report tickFailed, and refused action persistence reports actionFailed. Its full signature is onJournalProblem(id, code, [error, data]): a failure carries error and no data; an event — the expiry of a done job, expire and expired — carries data, the values the engine worked on, and error beside them where the event failed. Every failure is reported each time it happens, so a repeated failure stays in the log; a sink that wants fewer lines filters them itself. The serial helper reports nothing; its callers report their own failure codes. The callback’s optional error argument is the error’s runtime type and its whole message (StateError: Bad state: …, the type said once where the text already starts with it), never its stack; a runner must not put a key in an exception’s text. The state adapter (logJobReport) writes jobs.<code> with the job id: a failure as a warning with that error, an event at info with its values, an event that failed as a warning with both.

6.13 Media insertion — filmopen_insertion

Section titled “6.13 Media insertion — filmopen_insertion”

Insertion is separate from the storage and playback package (§6.9). filmopen_insertion is pure Dart; its permitted application dependencies are filmopen_jobs, filmopen_media, filmopen_store and filmopen_format. It imports their public barrels, never state, forms or the plug-in host. The moved importer, thumbnailer, stock thumbnails and local-file/off-thread IO/stub pairs keep their existing responsibilities. The public barrel exports the service, target, source and environment contracts and the legacy importer/thumbnail APIs; the runtime, replay helpers and journal checks remain private. The transitional importMedia facade and export were removed after architectural review when forms switched to the service.

A job’s attachment runs in the zone it was submitted in, whichever job’s completion or tick started it: what was asked inside a chat’s attribution lands in it, and a person’s job queued behind one has none.

The target is independent of the visible page. InsertionTarget captures the film key, entity stem, accepted media kinds, purpose and a TargetPlace: SlotPlace names a group of the entity’s media (MediaSlot.group) with its first/last insertion policy, BlockPlace names one of a block’s six media groups (§10.3) by the block’s id and the group, with the spans chosen before the render or upload — by the place’s chooser, once before any road (§9.3) — kept out of its identity, since one block group is one place whatever its entries cover — and its target’s accepted kinds default to that group’s (acceptedKindsFor), and TakesOnly names no entity field; TargetPlace.group is the group a place’s outputs become entries of — the slot’s, the block’s — and null for TakesOnly. No kind chooses the destination: every output of a job becomes an entry of the place’s group, as one block in selection order, and a place of another kind of slot is refused (invalidOutputType). These values round-trip as JSON. The target serializes its film key as project; the Dart property remains projectKey. The placeKey combines film, stem and canonical destination identity; insertion order is retained as policy but does not split one destination into different places.

A durable description does not preserve source access. FilesSource retains runtime inputs and copies byte lists. Its SourceDescription records each input as it is: its order, its kind (file, bytes or URL), its extension, its original name, and a file’s path or a URL’s address; bytes stay in memory. ProducerSource snapshots its request in memory and describes only its producer kind. Generated take file names, measured sizes and hashes are persisted as ingest intent. Every checkpoint and effect intent is logged as insertion.journal.write with the job’s id, its stage and phase and the whole payload, so that a job can be followed from the log alone; the jobs engine’s refuseCredentialKeys is the one check of a payload. A producer’s outputs made by a plug-in’s run — a render’s, every one of whose items names the same run ({ tag, stem, hook: "render" }, the stem a plug-in’s that carries the tag) and the same record of what it was asked (item) — are the run’s: their takes are named under the viewer’s handle for the plug-in (john123.zi), their record is a kind: "plugin" batch by that handle with the run’s plugin and hook, each item carrying what the run was asked — §12.2’s keys of it alone, so that nothing a producer says overrides what the file measures or the place — and neither the plug-in’s file name nor a nested provenance; the payload keeps the run’s three names once planned, never what it was asked, and a plan that names no run is an import whatever its outputs say after a restart. Outputs that name no run are an import as before; outputs that name a run otherwise — a tag that is no tag, a hook that is not render, a stem that is not the tag’s plug-in’s, no record, or runs and records that differ between them — are refused (invalidPayload) before anything is written: whose they are is never guessed. The payload names its idempotency identifier submission. The temporary journals with the former token and target projectKey payload keys have no compatibility reader. A producer’s recovery JSON is journaled as the producer gives it. JobUnavailable codes thrown by output capabilities, a producer’s state codes and a store’s codes reach the view and the journal as they are.

One binding is captured before submission yields. InsertionBinding holds the project, viewer, guards, locale, media store and size limit — the web’s 64 MiB (maxImportBytesWeb, the app’s own policy, since a web session holds every upload in memory), none on the desktop; the limit is part of its fingerprint, so a job persisted under the old desktop limit of 2 GiB is refused bindingChanged once. Its identity fingerprint covers the project key, viewer, guards.warnOnly and byte limit, and the payload stores it. Store availability and the manifest’s old data field are not part of the fingerprint: media belongs to the bound project’s own source. A new film or person on screen cannot redirect a running copy. Restoring unfinished mutation requires the same film and identity fingerprint; a changed viewer, guard policy or byte limit remains bindingChanged. A validated started producer whose complete attachment has already been verified and marked may restore acknowledgement alone in the same film, as described below. An inactive job may rebind to the current locale and machine settings. Active attempts retain the capture with which they started. The binding also carries the app’s write record (InsertionBinding.record, §7.5): every take record, batch and cleanup the job writes goes through InsertionBinding.writer(), which enters each write in the record before the source is asked, so that the next publication lays it over the index unread. The binding carries, where this platform has an engine, probeAudio: whether the clip at a local path has sound (an AudioProbe: audio, or the engine’s code and message), the probe of the engine the timeline measures with (mediaAudioProbeProvider, §19.13), read only when a clip is imported. Both roads measure a clip once, after its bytes are written, through the store’s local path: the direct road in _importOne and the replay in _one for a take it wrote; the answer goes on the item as audio — the record’s and the checkpoint’s — and entryOf writes it on the entry (Project Specification §9.0); no engine (no_probe, the probe’s unavailable included), media held without a local path (no_file, told by the source’s disk path without reading the clip) or an engine that fails to read the file (failed:<code>, logged at warning as media.audio_probe.failed with the file, the code and the engine’s message) leaves it absent, and the clip is imported unmeasured: the probe’s failure is not the import’s. media.imported carries audio, audio_reason and audio_ms for a clip. InsertionEnvironment supplies capture, noticeWritten (one publication with a listing for the film, where it is still open), the clock and the entity patch seam without an import of application state.

Restoration binds the opening before it waits. The initial restore call captures its opening snapshot synchronously, uses it while journal loading runs, then clears the temporary closure capture. The retained engine binder may be called on later reloads or takeovers and then captures the live film. A closed or different film returns projectNotOpen, leaving the attachment waiting for its film; a changed identity fingerprint remains bindingChanged unless the validated job needs only acknowledgement of its already verified attachment in that same film. Existing inactive runtime bindings also receive the newly captured environment while retaining available source capabilities. An opaque or malformed place key, or another job kind, cannot make insertion throw or resume unrelated work.

Exact accepted identity. InsertionService.insert has an optional, defaulted onAccepted notification. It reports one exact actionable insertion id once, after the engine accepts the submission. A throwing observer cannot undo acceptance, and is logged insertion.acceptedObserverFailed with its message. Stop uses the existing act(id, stop) door. insert and submit(target, source) are one door logged around — submit without the notification — answering the job’s id and throwing a refusal; logged insertion.submit.start {target, kind} (a producer’s request beside, an upload’s files — each input’s kind, name, extension and path or address), then insertion.submit {id, ms}, insertion.submit.refused {code, target, name, ms} (name the refused file’s, where there is one) or insertion.submit.failed {target, error, message, ms}.

Recovery association. At every producer checkpoint and effect intent, insertion projects the source’s producer kind and the recovery’s id into the reserved producerKind and renderId state arguments. Restoration recomputes this projection from the exact loaded insertion record, including terminal records and binding refusals. An id-only recovery is sufficient. Persisted association arguments are never authority: a record with no producer kind or no string id has them removed and leaves the job generic. Unrelated state arguments and every other engine view field are preserved. No place, entity or row order supplies an association.

The service owns the job, and callers can observe its place. InsertionService supplies insert, jobsFor, views, act, restore, filmClosed and close, backed by the jobs engine’s upload/producer runners. The application configures eight active attempts per film’s insertion:<film> admission lane. The journal retains that lane name across restoration. An attempt observes its producer under its live execution claim while queued or running at the provider; its subscription, checkpoints and shutdown draining remain the attempt’s. The provider’s queued state is presented as queued with waitingToRender, running as working, and collection as working with collecting. Local execution limits belong to the producer’s resource scheduler. A producer’s mutations share a separate FIFO gate keyed by film: allocation before reservations are copied, filesystem checks and reservation, ingest, batch publication, attachment, failed-producer rollback and stopped-producer settlement; an upload does not join it (below). Provider start, restore, observation, stopped-output collection and acknowledgement do not hold this gate. Gate ownership spans services draining in the same isolate; a queued mutation checkpoints waitingForFilm while another job of the same film holds the section, keeping Stop available during normal work. Its insertion tile and Activity row say that it is waiting for another job in this film, in the chosen language. The FIFO waiter checkpoints that waiting state once when it joins the queue, then awaits the preceding mutation without a heartbeat or timeout of its own. Cleanup waits safely with no further actions. It checkpoints its active claim again after acquisition before any file effect. A refused waiting checkpoint retains its FIFO link until the earlier writer leaves; only a process restart frees a disk write that never returns. Closing or restoring a service in the same isolate does not clear that film’s shared FIFO tail. Producer ingest and attachment recheck the captured film’s ownership after acquiring the gate. A closed session waits for its film; a replaced session retains its producer result for Stop without writing a new take or batch. Media attachment rechecks that ownership too, with its actions governed by whether the producer has already been acknowledged. Existing per-entity patch and draft settlement remain the only document writer. InsertionService.current starts as NoInsertionService; main installs the state-adapted application service (§7). Durable film keys select durable jobs; session keys select session jobs, which never enter the journal even if the engine has a directory. insert asked while the plug-ins’ settings are read again — a plug-in allowed a moment ago — is refused at once as settingsLoading (the environment’s capture throws it), logged insertion.bind.refused with the code, the reason, the target and the source’s description; nothing is submitted, its place says the plug-ins are not ready, and the person asks again (§7). Unsupported upload kinds are refused before a job or a write, by acceptsFile, the rule a place judges its files by before it submits them (§5.10, §9.3). A person’s files take the direct road (MediaImporter.import, below): each file is read, hashed and stored once as it passes, the copy’s fraction checkpointed for its tile (below), its thumbnail and the batch record written, then one checkpoint attaching keeps each take’s file name, its item number and its measured width and height with the record’s stem, and the attachment follows; Attach again and a restart attach from that checkpoint alone, never importing again. A failure during the import takes back what it copied (media.importFailed); the job needs attention with the refusal’s code and the file’s name, Retry importing anew, or ends failed with recordNotWritten. A stop during the import is seen between the chunks of the file being copied, so that file is never renamed into the media folder and its temporary goes, and the files the batch had finished are taken back (media.importCancelled); the job ends cancelled. Its files live in this session’s memory: a restart before the checkpoint needs attention as sourceUnavailable, and what the interrupted import had copied stays in the media folder as it is — a take with its record where the import had finished, never attached. A stop after the record keeps the batch and its takes, unattached — the stop refuses the attachment’s patch — and publishes them through the controller’s noticeWritten, the batch laid over the index from the record its writer entered it in, so the person finds them under Takes. The upload logs insertion.upload.start {id, target, source, viewer, reserved}, insertion.upload.imported {id, batch, items, ms}, insertion.upload.refused {id, code, name, message, ms} (or {id, code: sourceUnavailable, reason, ms}), insertion.upload.stopped {id, reason, ms} (with batch and items after the record), insertion.upload.attachAgain {id, batch, reason}, insertion.upload.failed {id, stage, code, error, message, ms} for an unexpected failure — answered writeFailed — or a refusal thrown, and insertion.upload.done {id, phase, code, ms}. For a producer’s outputs the internal runtime checkpoints intent before effects; the replay importer reserves the batch and take names before reading, then records each expected size and SHA-256 before writing. Replay reuses only a saved name whose bytes match both measurements, and checks the batch’s identity and item facts before accepting an existing record. A producer’s output lost across restart needs attention as sourceUnavailable; completed takes can be reconciled without its private source access. The record comes before the settlement (Project Specification §12.3): once every output is written, the batch record is written — each item numbered by its output’s place in the order given (a render’s by its place in the plug-in’s list; an output whose take was not written leaving a gap), and each naming the place’s group (none for TakesOnly) — and only then is the ingest settled and the attachment begun. While the record is being written the set of outputs is fixed (recordPending), so a restart never tries a failed item again; a restart that finds the record writes nothing and checks its items (_sameItems), and one that cannot write it — a store’s refusal, nameTaken, or a runtime input lost across a restart, whose original name and provenance the record takes (sourceUnavailable) — rolls back every output of the job, a producer’s partial ingest included, and ends the job failed with recordNotWritten and Dismiss alone. A settled job’s record is checked again on every replay, a changed one refused as nameTaken without rolling anything back. A stop asked before a producer’s record stops the ingest: its outputs, which a render paid for, are recorded as far as they were written, cancelled, or taken back where no record can be written; a stop asked while the record is being written stops only the attachment, since that write is fenced by its checkpoint and not refused by a stop. A stop no attempt settled — one recovered after a restart, or asked of a job no attempt is running — settles a producer’s outputs the same way before the producer is cancelled: its run, restored or heard again, is asked for its outputs once more — a render’s are still staged — and every take on disk as the plan measured it, its write noted before or not, is recorded as far as it was written, else taken back where the producer hands nothing over or the record cannot be written, so that no take is left without its record. A producer that says nothing within producerHeardWithin (30 seconds) leaves the stop to be tried again (producerOutcomeUnknown) with nothing taken back, as does a folder out of reach (rootUnavailable), the producer kept to hand its outputs over then; one whose work is not restored at all leaves its outcome unknown, the job waiting for the person with nothing left on disk; where the version is gone, nothing is recorded and the producer is stopped as before, what it wrote left where it is. A stop the running attempt settled stops the producer without settling again, and one tried again after its own settlement still stops it (stopSettled); a stop asked after the ingest had settled acknowledges the producer, whose outputs were kept, as the run would have (producerAcknowledged, kept in the journal) — after a restart too, from its confirmed marker or by acknowledging the same restored run; unconfirmed acknowledgement is recorded as uncertain, the captured film is told what was written and the job still ends cancelled, without claiming failed filesystem cleanup — unless its film had no folder and another film has since replaced it, when the stop lets the producer go; a producer already told is never stopped, since it holds nothing more. A stop the producer did not take — its cancellation answers that the same work is still held, a render whose stop is unconfirmed or was refused while it finished — leaves the job waiting as stopUnconfirmed with Retry and Dismiss, never cancelled: Retry runs the job’s ordinary attempt again, which follows the same run — reused in the session, restored from its saved recovery after a restart — and places what it hands over once, as any attempt does; Dismiss removes the job. A Stop after that Retry asks the producer again. Where a version is gone or no viewer is bound, the stopped settlement records nothing and logs insertion.settleSkipped with versionUnavailable or viewerUnavailable for what is missing. The cleanup logs insertion.cleanup.start {id, stage, producerKind, producerRecovery, producerCancelled, kept} before it waits on anything, and insertion.cleanup {id, stage, producerKind, producerRecovery, cancelAsked, over, kept, decision, code, ms} at every exit, the decision cancelled, stillHeld, attention, failed, or interrupted where a lost lease hands the cleanup to the next attempt; an attempt logs insertion.produce.attached {id, how, stage, producerKind, producerRecovery, producerCancelled}, how saying whether it reused the session’s run, restored it from its recovery or started it. A replay whose producer can no longer hand its outputs over — a render that failed or was stopped elsewhere, or that its manager no longer holds (producerFailed) — takes back what the job wrote before the restart, as a stop does, a record written before it standing as it is, and ends with the producer’s code. A film with no folder that another film has replaced — the bundled sample’s session copy, left for another film — is never written with a producer’s outputs: the job, once they are there, needs attention as filmClosed, the outputs kept with it until the person stops it, which lets them go — a producer told before a closing refused its attachment holds none, and its job offers Dismiss — and a producer’s attachment refused as not open ends the same way rather than waiting for a film that never opens again; a producer’s outputs are held back too while no film is open — closed, or another loading — the job waiting for its film (projectNotOpen, with Attach again and Stop, or Dismiss once its producer was told) rather than writing into a copy it cannot tell is kept, and the film another one replaces asks their Attach again once to say so; a render is never told its outputs were kept while its film is gone, and the application says, as each such job ends, that it did (§9.7); an upload, which nothing paid for, waits as it always has, one closed and not replaced — Close project keeps it — is waited for as before, and a folder film is written to whether it is open or not. A take written before a retry or a restart whose bytes no longer match what was measured is no longer the job’s: neither recorded nor taken back, while a producer’s other outputs are recorded as far as they were written (partialIngest); any other failure to read it leaves the job to be tried again, the take still the job’s. A record on disk is never undone: one written before a restart the journal did not hear of, or before a failure the stop then met, stands as it is, and nothing it names is taken back — on a stop, and on a replay that finds a take it names changed, which ends nameTaken as a settled job’s does. A rollback frees the set of outputs again, so a later attempt ingests anew. A journal whose target no longer decodes — the retired legacy place, the old slot discriminators — ends invalidPayload, dismissable; it is not migrated. Each take written logs media.imported once — an upload’s with its stem, kind, original name, where it came from, byte count, SHA-256 and whether its thumbnail was made, a render’s replay with its stem, kind and byte count; attachment retry does not emit another. A producer’s ingest can keep successful items and report partialIngest.

Code that inserts reads one job. jobView(id) answers one job’s view, read-only, and needsAttention is a phase like any other; the default service answers none, and the application’s service projects the view with its render attention as it projects its views. It logs insertion.lookup.start and insertion.lookup.done {found, ms} — never the id asked, a caller’s string until it is found. A done job its place has drawn is dismissed by that place (§9.3) and leaves the views, and then has no view.

Reopening attaches nothing on its own. A job waiting for its film (projectNotOpen, with Attach again) waits for the person’s Attach again after the film opens again; restore logs insertion.restore.start {film, durable} and insertion.restored {film, jobs, replaced, ms} with each job’s phase and stage. The one exception is a render waiting for a film with no folder that the opening replaced, whose place never shows again: the opening asks its Attach again once (insertion.replacedFilm {id, film, accepted, code, reason}), which ends it filmClosed with its Stop, so that the application says so (§9.7). Runtime inputs end with their job. Successful terminal persistence releases the engine’s binding, and terminal restore does not reconstruct one. The insertion service releases its private source and producer cache when a job becomes terminal or is dismissed. A failed terminal commit retains the binding for retry; unresolved jobs retain available runtime capabilities. Closing releases remaining in-memory ownership.

Attachment is proved by the patch’s hand-back (insertion_attach.dart). The service patches through InsertionEnvironment.patchEntity, and InsertionPatchResult succeeds only with the entity JSON as the writer wrote it, accepted by the attachment’s own check; nothing is read for it. Each output becomes a MediaEntry of the place’s group through withEntriesInserted — reference its take’s stem, batch the record’s stem and item its n there, and in voice_samples the voice_binding its run’s output gave it (Project Specification §9.1: the platform, the preview it is or the voice that spoke it), read as the format reads one, planned with the job so that a restart attaches the same, and nothing else an output says of its entry — as one block, first or last as the place says; a reference the document already holds anywhere — in any group of the entity or of any block — is not added again. The entry carries its facts (§9.0), read from the job’s payload item, where the replay planned them so that an attach after a restart writes the same and nothing is invented: duration_ms 0 for a still, else the plug-in’s reported value, else absent; width and height as the replay measured a picture, else as reported, else absent — the item keeps of the plug-in’s report its three whole numbers alone; stage the stage the render was asked at, absent on an upload; unreviewed: true on every render’s output, a voice preview included, and never on an upload; kept_at never. A BlockPlace‘s outputs go first into its group of the first block of that id (no such block, or a media that is not an object, is wrongShape), each with the spans chosen where the group allows that shape and else the group’s default — a disallowed shape is dropped, never refused — and no preview; the patch’s key is blocks. A render answering two kinds is attached whole into its one place: a clip and a soundtrack for one block are two insertions, each into its group. Media reaches done only when that document names every intended reference. A stale or refused attachment offers attachAgain, which retries attachment without another put or render — a producer’s after verifying its existing take and batch, an upload’s from its checkpoint. projectNotOpen leaves the job waiting for the person’s Attach again. Successful items from a partial producer result are attached, while the job retains partialIngest and the missing items’ outcome. TakesOnly tells the captured film after ingest (InsertionEnvironment.noticeWritten: one publication with a listing, the batch laid over the index unread, nothing read) and finishes without patching an entity; an attachment to a form publishes the batch with the patch’s own gesture.

The app owns insertion independently of its cards. Forms submit a destination and source through the service, then draw public JobView data and invoke only the supplied actions. The former importMedia facade is gone. The plug-in host submits its render outputs through InsertionService with a ProducerSource; the application supplies ManagedRenderProducer, and each output’s group is retained through the insertion contract (§6.11).

The direct import path is an upload’s road. It retains its collision refusal and does not join the service’s mutation gate; it shares the service’s reservation set (reserved:), adding the batch id it proposes before anything is awaited, so that two imports in flight together never take one id while neither record is in the index (media.batchIdTaken where a take of an id is in the store already, media.importStart with the id, the inputs, the groups and the reservations, media.importDone with the items). It is MediaImporter.import(entity, inputs, viewer:, groups:, run:, cancelled:, reserved:, onProgress:), groups one per input — the group its item records, or null — the takes and the batch the viewer’s for an upload, and <viewer>.<tag>’s, with a kind: "plugin" batch, for a plug-in’s render, run being its PluginRun. A MediaInput is a file on this machine, bytes the caller has, or a URL (http/https, the content type checked before anything is stored, fetched with a client of its own — debugMediaHttpClient’s stand-in in a test — logged media.fetchStart {url, name} and media.fetchAnswered {url, from, status, contentType, length, ms}, from where redirects the client followed ended elsewhere; the client closed once whatever the outcome). Every kind is settled before a byte is written; then, per file, in this order: the bytes stream into the store while their SHA-256 and their size are measured — a picture small enough to thumbnail is kept as it passes, a clip is not, so a file larger than memory copies through — then the thumbnail into the project folder, then, once every file is in, the batch record through ProjectWriter.writeImportBatch, its items numbered 1..k in the order given, the take’s file number staying per purpose. A picture too big to hold has no width and height in its item, which §12.2 makes the right answer: measured on ingest, never invented. The importer never touches an entity’s file: it answers the ProjectMedia. The insertion service attaches through the state’s bound patch door, which uses the open draft when present, preserving autosave, the identity lock and stale refusals. A record that cannot be written refuses the import: the files and thumbnails the action wrote are removed and recordNotWritten is thrown, so no take arrives without its record (§12.3). Refusals are codes (MediaImportProblem: notMedia, empty, tooLarge, nameTaken, rootUnavailable, noViewer, badUrl, unreadable — a file gone between the picker and the import — and recordNotWritten), never sentences, and neither a URL nor a server’s words ever reach an exception or a message; the log carries them (media.importRefused with the address, and for an answer that is not 200 its status, reason, contentType, redirect location and its body whole, for a 200 refused as not media, empty or declared too large its status and contentType, and for one refused as its bytes pass — too large or empty — the address; media.fetchFailed with the address and the error’s message). A stop is seen between chunks: cancelled is read before each file, before each chunk is hashed and after its hash, and after each file’s put; seen in the stream it ends the stream with MediaImportCancelled, a WriteCancelled of the store’s, so the store’s write closes its temporary, renames nothing and deletes it before it returns — logged folder.write.cancelled {kind, path, step, bytes, waitMs, ms, reason} at info, not folder.writeFailed — and the path is never added to what the batch wrote; the files the batch finished, and a file whose put ended as the stop came, are taken back, then media.importCancelled {batch, stem, stage, bytes, of, inputs, written, thumbnails, rollback, notRemoved, ms, reason} at info. A stop inside one chunk (4 MiB from a file on this machine) is not seen until the chunk has passed, and a write waiting in the media queue behind another polls nothing until it enters. An unexpected failure keeps media.importFailed at warning, with its stage, the file and its bytes. A file on this machine is read in chunks of 4 MiB (localReadChunkBytes), not 64 KiB: the app pays a wait for every chunk on its own isolate, so a large clip copies in few, large chunks. The read logs media.localRead.open {path, chunkBytes, reason} and media.localRead.end {path, outcome: complete | cancelled | failed, chunks, bytes, ms, error, message}, at warning when the file failed; cancelled means its reader stopped listening (a Stop, or a failure further on, which logs its own cause). The hash is taken off the painting thread: MediaImporter.startHasher starts one ChunkHasher per file — a worker isolate on every platform but the web, where the stub hashes inline as the bytes pass — each chunk sent, its acknowledgement awaited, then handed to the store, so one chunk is in flight; the digest is taken once at the end and the hasher ended in every case, logged media.hash.start {batch, take, input, backend, of} and media.hash.done {batch, take, input, backend, bytes, ms, outcome: complete | stopped | failed, error, message}, its ms the file’s whole copy — read, hash, acknowledgement and write. A render’s replay goes through the same measure, with no stop and no progress of its own. An upload’s tile shows its copy’s progress (onProgress:): bytes written across the selection’s files over the lengths they declare before a byte is read — 0 as the copy starts, then at most once a second when it has moved 1 % or more, then 1 when the last file’s copy ends, before the thumbnails and the record — each one logged media.importProgress {batch, stem, fraction, bytes, of, ms, reason: start | tick | end} as it is given and checkpointed with that progress — its state read as the journal writes it: working with Stop, or, once a stop is asked, stopping with no action, so that a fraction queued behind a Stop’s write never puts Stop back, logged insertion.journal.write {id, stage, phase, payload, progress, reason: progress | stopping} —, a checkpoint refused logged insertion.checkpointFailed {id, stage: progress, fraction, error, message} at warning; a selection with a file that declares no length — an address — gets none and keeps the spinner (media.importStart says which, progress and progressReason). A film closed while its upload copies — or another film opened in its place — is told to the service by the application’s binding with the film it was (filmClosed), logged insertion.binding.filmChanged {from, to, closed, project}, and the runtime logs insertion.filmClosed {film, durable, jobs: [{id, phase, stateCode}], decision: uploadsRunOn, reason}, its jobs matched by the film their place names; nothing stops, and an upload that ends with its film closed waits for Attach again.

The thumbnail (filmopen_insertion/thumbnailer.dart, §6.3): within 300 × 300, no crop and no enlarging, EXIF orientation baked in, transparency flattened on the theme-neutral grey #808080, JPEG at quality 80 or 60 when it exceeds 48 KB. Decoding runs through Isolate.run on IO platforms; without a worker, the derivative defers to stock artwork. Checked PNG chunk streams with one IHDR, JPEG SOF, GIF canvas, supported INFO-family BMP and static WebP headers measure positive source dimensions without allocating the decoded bitmap. Each axis is at most 0x7fffffff, and the source area is at most maxThumbnailPixels; the area check divides the pixel bound by one positive axis instead of multiplying untrusted dimensions. Dimensions outside these bounds yield neither measured dimensions nor a photograph thumbnail. BMP palettes and offsets are bounded; WebP chunks, competing headers and actual encoded dimensions are checked, with raw VP8 scaling bits refused. CORE/short BMP layouts and animated WebP without a proven frame/canvas bound defer to stock. Unknown or malformed bounds never fall through to an allocating decoder probe. The current decoder has no reduced JPEG or PNG mode, so supported pictures decode whole on the IO worker up to maxThumbnailPixels of 80 million source pixels, without an additional decoder-input byte ceiling; beyond that pixel bound they use stock artwork, while the importer still drops retained picture bytes above 128 MB. Dimensions that pass these checks remain measured ingest facts when a later derivative step defers. Bounded supported pictures decode their first frame alone, retaining existing orientation, colour, fitting and transparency. The PNG library has no decompressed-stream output ceiling, so these guards do not establish a complete malformed-inflate memory bound. The importer takes the decoded thumbnail where a held picture decodes within these bounds, else the stock artwork of its kind; a decoder that throws is logged (media.thumbnailFailed, code decode) and gets the stock artwork too, and each take logs, after the write, media.thumbnail {stem, kind, picture, source: decoded | stock, reason, bytes, decodeMs, ms}, the reason one of decoded, not a picture, picture not held, no decoder on this platform, not decodable within the bounds and decoder threw. Deferral supplies stock artwork now and creates no regeneration queue; only the importer stores the result. Clips, sounds and other files retain their existing stock artwork, stored in thumbnails/ like any other.

Partial producer ingest settles its successful indexes and per-item failure codes durably before creating the batch, whose status is done where every output was ingested, failed — an ingest problem, with the items that were written — where some were not, and cancelled — a stop, with the items that were written — where a stop ended it. Recovery and attachment retry keep that subset and do not retry missing producer outputs; no acknowledgement releases their source. A missing batch whose original names/provenance have been lost holds as sourceUnavailable, preserving takes. Existing batches are checked against durable item facts without reconstructing private names. Malformed planned paths, identities, indexes or measured facts are refused before replay performs any effect.

Importers store takes directly in media/ and record their file names as batch output. The insertion service uses each take’s extensionless stem as its entity reference. Batch documents are written into data/; thumbnails use thumbnails/tn_<stem>.jpg relative to the project root. Replay uses the same file names and folder rules.

filmopen_sync is pure Dart, depending on filmopen_format, filmopen_jobs and crypto. Its public contract describes a SyncTarget through inventory, find, committed, streamed put and streamed fetch operations, with entries, receipts and stable failure codes. SyncFailure distinguishes a retry, a required sign-in and a permanent failure. The package supplies these contracts, lane and retry rules, inventories and differences, payload validation, a folder target, local byte streams and SyncService with its transfer runners. SyncEnvironment supplies the current source, target and clock through a memory-only SyncBinding, with isOpen(projectKey) identifying the matching open, nonloading film independently of its target. The state’s createApplicationSyncService constructs an inert engine with rules.capacities, an application-supplied targetFor seam and a two-second settle by default. A supplied sync directory holds the journal, with the ledger in its ledger child and media parts in its parts child; without a directory these stores are in memory, and session films always use the service’s separate memory stores. Storage construction touches no file; the entrypoint installs it after insertion, as described below. ContainerSyncEnvironment captures only an open, nonloading film with the matching key and an available target. syncJobsProvider subscribes before seeding and suppresses equal per-film snapshots. SyncAppBinding coalesces notifications in a microtask: opening restores, later changes reconcile, closing captures nothing and stops no jobs, and reopening restores (including its reconcile and waking filmClosed work). Loading makes the binding unavailable; readiness restores even the same film so sends that waited as filmClosed wake again. An ordinary write reread stays ready and reconciles. Its idempotent close cancels the arrival timer, detaches project listeners and starts arrival-subscription cancellation and service shutdown together, with a ten-second default grace period through ExitTasks. Subscription cleanup cannot delay the start of the service grace. Journal and service reports log sync.<code> with the job’s id — the film’s key where the report is the film’s, as ledgerUnreadable is — the error the report carries, as it is: from the jobs engine and its journal, the error’s type and its whole message; the engine’s expiry of a done send, expire and expired, at info with its values, through the same logJobReport as the insertion’s.

LocalInventory.read lists a ProjectSource once and measures each retained file: the root manifest, flat files in data/ and flat files in media/. Thumbnails, nested files and other paths are excluded. Documents, including the manifest, are hashed with MD5 — from the text SyncService(heldTexts:) gives where it holds the document and that text’s UTF-8 length is the file’s, unread, and read once otherwise — a byte-order mark the reader dropped gives the held text another length, and so does a change made outside the app that changes the length; a change outside the app of the same length is hashed from the held text until the folder watch reads it again; media bytes are never read for an inventory. The application gives the open film’s view (§7.5): the published index with the app’s landed writes, so the local side is the film as every decision reads it; each pass logs sync.inventory with every path’s hash or size and the documents it read. When a document’s send is done, its hash is compared with the document’s current held text, under the same length test (sync.send.compared, with the two hashes, the two texts and where the local one came from); a differing one — an edit made during the upload — asks a pass that sends it. SyncDifference.between returns pending sends, pending fetches, conflicts and newly observed agreements. It decides each path once, with no deletion or rename. Sends and fetches put the manifest first, then sorted data/ paths and sorted media/ paths; conflicts are sorted by path. A target must supply a document’s MD5: a missing or malformed hash is invalidTransfer, never agreement inferred from equal lengths.

For a document, L is the local hash, T the target hash and S the last agreed hash. The decision uses those values alone:

Local and target state Decision
Present locally, absent at the target Send the local document.
Absent locally, present at the target Fetch a new file without replacing a local name.
L = T Record that hash as agreed when needed; move no bytes.
L = S, T differs Fetch a replacement carrying the local hash that authorized it.
T = S, L differs Send the local change.
Both differ from S, or there is no S and both are present but different Report a conflict; send and fetch neither copy.

Reconciliation samples each film against a service-owned metadata revision captured before either inventory is read. Runner agreements invalidate older samples before their write is queued, including equality, verified sends and receives, and Keep mine. Reading ledger state and committing observed equality are serialized with those writes. A revision change during any metadata wait discards the sampled decision; the existing reconciliation queue coalesces one fresh pass without a polling timer. Once the metadata transaction accepts a difference, each submission checks that its own path has no newer agreement or dismissal and is not being dismissed. An unrelated path completing does not discard the remaining accepted work; a changed path is skipped and gets a fresh pass. An old equality observation therefore cannot replace a newer verified agreement and authorize receiving over a later local edit. This ordering belongs to the one application-owned service; it is not a cross-process transaction over both copies.

A conflict set aside at exactly these two hashes is omitted from the conflict list, while still blocking both directions. A change to either hash raises it again. Equal copies clear an old set-aside entry even when the agreed hash was already correct. Media uses presence and size, with no agreement ledger: a missing local name is fetched, an existing local name is never fetched or replaced, and a differing target size remains a pending send governed by the target’s existing-name refusal.

Agreement storage. SyncLedger records agreed document MD5s and conflicts set aside at their local and target MD5s. agree, agreeAll and decide clear any set-aside entry for the paths they record. A reconciliation’s whole ledger transaction is one decide: the agreements and asides read once, handed to the pass’s decision, and the equal files it found committed in one write in the same serialized step, before jobs are submitted; the pass logs sync.reconcile.ledger with the counts read, the asides’ paths and the agreements it is about to write (toAgree), or superseded: true where a newer agreement or dismissal overtook it. The memory implementation keeps data for the run. inDirectory(directory) keeps one file directly in that ledger directory, named <sha256(projectKey + newline + targetId)>.json. Its JSON contains only schema: 1, an agreed map of relative document paths to hashes, and an asides map of paths to {mine, theirs}. The film key and target identity are hashed for the filename and absent from the contents. Paths follow payload safety rules; hashes are 32 lowercase hexadecimal characters. Returned maps cannot mutate the stored state.

Ledger read-modify-write operations serialize per file across store instances in this process. A write flushes a sibling temporary file and renames it into place. A file that cannot be read or parsed, has an unsupported shape, or contains an unsafe path or hash is empty for this run and reported once as ledgerUnreadable, with the film’s key as the id and the target, the file’s name and the error’s words. Subsequent agreements may populate that empty state; a reporting callback cannot change its outcome. SyncService accepts an optional ledger, defaulting to memory, and uses a separate in-memory ledger for session films even when disk storage was supplied. A verified document send, including a matching-target completion after a lost answer, records its immutable sent hash before its completion can cause another reconcile. It never records a newer local edit as though those bytes were sent. Ledger removal on film deletion or stopping sharing is not provided.

Receiving contracts and parts. SyncTarget.fetch(path, from:) answers SyncBytes: the actual starting offset, whole-file length, whole-file MD5 where known and a byte stream. A target may answer from zero when it cannot honor a requested offset. A file absent under a fetch is retryable unavailable, so the next attempt’s find can establish whether it is gone; an offset outside the file is invalidTransfer. The consumer owns and cancels the returned subscription. The folder target streams from the requested offset, reports document MD5s and checks path confinement again when the stream is subscribed; filesystem failures become unavailable. The Drive target uses ranged downloads, returns zero when Drive ignores a range, reports its metadata MD5 and holds its client for the receiving stream, including bounded cleanup when nothing listens (§6.10).

SyncParts holds media prefixes separately from the film. Its memory factory touches no files; its directory factory stores <directory>/<film>/<id>.part, where film is the film key’s 64-character lowercase SHA-256 and id the job’s 32-character lowercase hexadecimal identifier. Any other identity is refused as invalidTransfer before filesystem access. held measures the retained prefix, append flushes before completing, read starts at the first byte and discard removes only that job’s part. Operations are ordered per part, so a film’s jobs write their parts at once; keepOnly is ordered only against that film’s other sweeps. A stream error retains and flushes the bytes already appended. keepOnly removes only regular files with valid part names for that film whose ids are absent from its keep set; other films, unknown files, directories and links are preserved. Linked film directories and part paths are refused. The service accepts an optional parts store, defaulting to memory, and always gives session films a separate memory store. After a durable film’s engine.restore, it sweeps that film’s parts, retaining fetch jobs in every phase except done, failed and cancelled. Other films’ parts are untouched; session restore never invokes the disk sweep. A cancelled or waiting attempt keeps its prefix. Publication normally removes its owned part; a cleanup failure is reported and leaves that part for the ordinary durable-restore sweep, without undoing the verified copy. A permanently failed fetch or the next durable restore after dismissal also removes its owned part.

SyncLane names sync:media and sync:data, with send kinds sync.media and sync.document and fetch kinds sync.media.fetch and sync.document.fetch. laneOf recognizes all four kinds, isFetchKind distinguishes receiving and isSyncKind excludes unrelated jobs. SyncRules.capacities defaults to four media files and one document. Retry delays start at five seconds and double to fifteen minutes, with a fifth of jitter and no delay beyond that ceiling. The rules also carry the attention threshold of eight failures, a zero settle delay and a progress step of 0.02.

FolderSyncTarget implements the target over an existing folder. Its inventory and find report the manifest and flat documents with MD5, and flat media with size and no MD5; thumbnails, nested files and symbolic links are excluded. Its inventory resolves the root once and reads each listed file as listed; a file gone since the listing is left out. A missing root is unavailable with a retry verdict. committed returns zero, so this target restarts incomplete sends from the beginning. Paths are checked before writing and again before publication; traversal and linked destination directories are refused.

A folder send streams into a temporary file, verifies its length and, for a document with an expected MD5, its content, then publishes the complete file. Documents replace their destination by rename, leaving the previous document intact if streaming fails. Media are published with a native hard link that refuses an existing name, including one another program — a teammate’s sync client — creates during the send or after the last check; no check-then-rename sequence can overwrite that file. Unsupported hard-link publication is publishUnsupported with a permanent verdict. The native call uses SDK dart:ffi inside the I/O implementation and adds no package dependency; dart:io has no create-only rename or link. A file-system failure is unavailable with a retry verdict, carrying the system’s words as its cause, which every '$error' of the sync logs. Incomplete sends leave no new destination and clean their owned temporary part while its parent remains confined; moved or redirected staging and unexpected contents are preserved.

The folder target, local byte stream, ledger store and parts use conditional I/O and stub files (folder_target*.dart, local_bytes*.dart, ledger_store*.dart, sync_parts*.dart). openLocalBytes streams from ProjectSource.diskPath when available, starting at the requested byte offset; otherwise it reads the source’s bytes and yields chunks from the same offset. The public barrel compiles for the web, where folder-target operations answer unavailable with a permanent verdict.

The payload contract contains schema, required direction (send or fetch), lane, project-relative path, size, MD5, replacement policy, target kind, target identity (targetId, a string of 1–128 ASCII letters, digits, underscores or hyphens), failure count and stage. The service writes SHA-256 of the target identity into payload targetId, as every queued payload holds it, and runner identity checks compare that hash; the existing engine place key retains the target’s original identity. The film key remains solely in the engine record’s projectKey and placeKey metadata, outside the payload. checkSyncPayload applies the shared credential word rule and validates allowed fields and types; a path must be a flat project-relative file of its lane. A checkpoint map holds what its target wrote, and only a credential key in it is refused.

A send has stage queued or sending, may carry sent and a checkpoint, requires MD5 and replacement for documents, and has neither for media. A fetch has stage queued, receiving or conflict, may carry received between zero and its whole size, and refuses both sent and checkpoint. Fetch MD5 is required for documents and allowed for media; only a replacing document fetch carries localMd5, a 32-character lowercase hash, and its presence makes replacement true. A conflict is a document fetch with that local hash. Send payloads refuse received and localMd5. A journal payload lacking direction is unsafe: an old send needs attention as sendFailed, Retry fails the same check, and Dismiss removes the job rather than migrating it.

One job per pending file. SyncService.reconcile compares the source and target inventories with their real agreement ledger, commits newly observed equality, and submits fetches, sends, then conflicts to their shared lanes. A conflict is a document-fetch job at stage conflict, carrying the current local and target hashes. A closed or loading film queues nothing and retires nothing. An open film without a target retires all its sync jobs the engine holds and queues nothing; a film whose restore failed at opening keeps its journaled jobs, which the engine does not hold, until an opening whose restore succeeds retires them. Reconciles of one film run serially, with overlapping requests sharing one queued follow-up pass. Every duplicate check reads engine.snapshot: a media-send or any fetch place key is [projectKey, 'sync', targetId, path], and a document-send place key adds its MD5. Only jobs of the current target block duplicates. A media job in any phase except done blocks that path; a live document job blocks the same MD5. A failed, cancelled or attention-requiring document blocks its path regardless of MD5 until dismissed or retried. A fetch or conflict is blocked by any sync job of either direction on its path except a done job; any fetch job except done also blocks a send on that path. Document send identity remains the submission identity when its content changes before sending.

A job belongs to its target. SyncTarget.identity is opaque and stable: a folder target hashes its canonical root with SHA-256, in hexadecimal; a Drive target uses its folder id. reconcile and restore retire jobs whose identity differs from the current target, or every job when the open film has no target. Retirement presses Stop where offered, else Dismiss where offered, and does nothing where neither is offered; a job already stopping finishes and is retired at the next pass. Every acknowledgement is checked; refusals report retireRefused and do not abort the pass. The runner checks identity at the start and between chunks: another target, or no target while the film is open, returns cancelled. A film that is closed or loading instead waits as filmClosed, preserving checkpoints and resuming on reopening.

The content at send time. All four sync kinds are interruptible and share a single attempt gate for payload validation, lane hold, open-film binding, target identity and cancellation; none has an engine cleanup runner. Each attempt captures the film’s current binding and asks the target’s find before sending: matching media size or matching current document content settles a lost answer without another put. A missing source file needs attention as fileGone; changed media size is fileChanged, both with dismissal. A document is read into one immutable snapshot that supplies its current size, MD5 and bytes; changing it discards any checkpoint for older content. The first send checkpoint includes the relative path as reference. A saved target checkpoint supplies committed, and streaming begins at that offset. The completed receipt and byte count are checked before the job finishes. After a document reaches done, its current local hash is compared with the sent hash; only a difference triggers another reconciliation. This settles changes back to a previously queued value without changing place keys or bypassing live-job deduplication.

Document receiving and conflict choices. A fetch asks find before taking bytes; a missing target file fails as targetFileGone, and changed target length or MD5 updates the payload. A document is re-decided from fresh local, target and agreed hashes: the local side is one snapshot — the text the app holds where its length is the file’s, else the file’s bytes read once and decoded as the source’s reader decodes them — whose hash decides and whose text is the guarded write’s expected text, logged sync.fetch.compared {film, id, path, mine, theirs, agreed, from, action}; a write made since refuses as stale (sync.fetch.stale {film, id, path, from}) and the fetch decides again. Equality records agreement and finishes without a write. A target still equal to the agreement finishes and arranges a reconcile after its done view so the local change can be sent. A missing local copy is created, and a local copy still equal to agreement can be replaced. Other divergence checkpoints stage and state code conflict, the relative path as reference and {mine, theirs} in stateArgs, then needs attention with takeTheirs, keepMine and dismiss. Those hashes and the reference survive journal restoration; hashes are never presentation text. A no-copy completion checkpoints only a static outcome in stateArgs: alreadyAgreed when the copies are equal, or keptLocal when the local copy is kept, including Keep mine and a target still equal to agreement. The engine preserves that outcome through completion and restoration. Normal receiving checkpoints replace the arguments before a later actual copy, so an earlier outcome cannot describe that copy. These outcomes describe the final decision, not a lifetime receipt history: after a publication interrupted before completion, recovery may find equality and finish already agreed.

takeTheirs authorizes a fetch under the fresh local text’s guard. keepMine records the current target hash as agreed, finishes without a write and arranges a post-completion reconcile to send the local copy. An accepted conflict dismissal records the checkpoint’s two hashes as an aside before act answers. Dismiss claims the path and invalidates older inventory decisions before its first asynchronous wait. Removing the row and persisting its aside share the film’s metadata ordering; the claim remains until that transaction finishes, and is released even on failure. An overlapping reconcile cannot recreate the unchanged conflict in the gap between removal and persistence. Retirement uses the engine’s action directly and creates no aside. Take theirs and Keep mine re-decide current contents first: equality or a one-sided change can settle a former conflict without applying an obsolete choice. Dismiss records the displayed pair without reading either side again; its aside suppresses only that exact conflict at a later reconcile.

A document is collected in memory and verified against its whole length and MD5 before strict UTF-8 decoding. Invalid UTF-8 fails as invalidDocument; a leading UTF-8 byte-order mark is retained so publication preserves the target bytes. run.effect persists intent before writeText, using create: true for an absent name or expectedText for a replacement. A fresh conflict choice is durably consumed before target or local reads. A receiving retry may retain Take theirs only while the local hash still matches the snapshot it approved. A refused stale write durably revokes that choice before repeating the decision, so an intervening edit remains protected even if a later read fails, the job retries or the application restarts. Text guards compare the source representation against the raw snapshot with at most the one leading BOM consumed by the source decoder; an additional leading BOM remains content. A successful guarded write emits one arrival immediately, then records the target hash. If that ledger write fails, the job retains the normal receiving-failure and Retry path; Retry can record observed equality without publishing or announcing the file again. A storage failure after publication therefore does not leave the open film unaware of bytes that already landed. Fetching is a copy through ProjectSource.writeText or writeBytes, not a person’s edit through ProjectWriter: its author, viewer and validation rules govern editing. Received files are read like other files on disk, with malformed contents becoming the loader’s warning rows.

Media receiving and verification. A fetch refuses a local media name as localNameTaken before asking for bytes. It resumes at its part’s measured length, discarding a part longer than the fresh target size. A response that starts at zero instead of the requested offset discards the old prefix; another unexpected offset is invalid. The retained prefix is hashed from its start; an attempt’s incoming chunks go to one append, opened at the first chunk and flushed once when the attempt ends, however it ends, and logged as fetch.part with the id, path, offset, bytes, outcome (flushed or writeFailed, with the error’s type and text), milliseconds, MB/s and flushes. The reply is read no faster than the part is written, and a failed write ends the fetch at once; checkpoints record receiving, the relative reference, received and progress at the rules’ step. A complete retained part is verified directly, avoiding a request at EOF. The completed length and every available whole-file MD5 must match before writeBytes under run.effect with create: true; a concurrently created local file fails as localNameTaken and is preserved. Publication emits one arrival before discarding its part. If only that cleanup fails, the verified publication still completes; partCleanupFailed reports the job id and error type without paths or error text. A diagnostic callback cannot change completion. The retained done-job part remains eligible for that film’s ordinary durable-restore sweep, which still touches no other film. A length or hash mismatch discards the part and retries as receivedMismatch with the usual failure count and attention threshold.

Every returned byte stream is subscribed and cancelled in a finally, including rejected metadata, early failure and interruption. Cancellation races a pending stream read, so a stalled transport can release its client. Closing the film is checked at chunk boundaries and waits as filmClosed; lane holds, user stops and service shutdown retain incomplete media parts. SyncService.arrivals is a broadcast stream of SyncArrival(projectKey, path) for successful writes, closed with the service; equality and conflict decisions without a write emit nothing. The service itself does not touch forms or the tree.

NoSyncService starts no listener, opens no storage and schedules no work. restore and close complete without doing anything and reconcile returns zero. jobsFor returns an empty list; views and arrivals are empty streams. Every act returns a refused acknowledgement with code unavailable. These answers let callers use the same service interface when sync is unavailable.

Arrival refresh. SyncAppBinding listens only for arrivals belonging to the open, ready film. An arrival carries a document’s text as a reader of the written file decodes it, a leading byte-order mark consumed as the loader’s reader consumes it (SyncArrival.text; null for a medium). A text is entered in the write record as it arrives, through ProjectController.noticeWritten(written:, gesture: 'sync.arrived', publish: false), so the record orders it where the folder does: an own save after it wins, and one before it is overwritten by it; the view holds it at once, and the folder watch finds it the app’s own. Each arrival restarts a 300 ms quiet interval; its expiry asks one publication for the burst, which lays the texts over the index unread and reads again a path that came without its text. sync.arrivals {film, paths, landed, read} and sync.arrivals.done {film, published, ms} bracket it. An arrival while a publication runs retains one follow-up burst: if its quiet interval has elapsed when the current one finishes, the next starts then; otherwise its remaining interval is preserved. Publications from arrivals do not overlap. An arrival for another film, a closed film or a loading film is ignored, and changing or closing the film discards its pending burst’s publication, its texts staying entered under the source they were written to (sync.arrivals.dropped {film, paths, entered, reason}); an arrival for another film or after the binding closed is sync.arrivals.ignored, and a burst due while a publication runs is sync.arrivals.held until that one ends. A publication in progress uses the controller’s own one-at-a-time order. A failed publication reports sync.refreshFailed {film, paths, type, error, ms}. The publication’s normal project notification reconciles the film; no timer polls a target.

Progress and recovery data. Target checkpoint callbacks are validated and copied before their journal writes are queued. Checkpoints passed to committed and put are separate deep copies, so a target cannot mutate a later journal snapshot through an input map. Every outgoing payload snapshot is validated again. The stream waits for those writes at chunk boundaries before sending more bytes. Progress callbacks update persistence only at SyncRules.progressStep intervals, with completion reaching 1.0. Cancellation is checked between chunks and returns cancelled; the engine then applies a user stop or lane hold while retaining safe recovery data. A checkpoint that holds a credential key needs attention as unsafeCheckpoint and is reported without retaining it.

Lane holds. The service’s one hold is the sign-in’s: a target’s sign-in refusal holds its lane as signInNeeded, offering only sign-in/stop, until an accepted sign-in action lifts it; a reconciliation never releases it. A hold interrupts sends and fetches in its lane, all interruptible without cleanup runners; targets keep partial recovery bytes private and publish only complete files. A cancelled attempt requeues with its safe checkpoint, so releasing the hold resumes a send from committed or a fetch from its part length. A media hold leaves the data lane running, and Stop cancels a held job. A sign-in acknowledgement applies only to the hold it addressed, so an older accepted action cannot erase a newer hold. The service reapplies the hold after an acknowledgement, including a refusal, and checks it again at runner entry before any target effect.

Failures and reopening. Retryable target failures increment the persisted failure count, use the rules’ exponential delay and need attention with retry/dismiss at the configured threshold. A sign-in failure holds its lane as signInNeeded with sign-in/stop actions and answers a retry with zero delay, so every due job immediately exposes that hold; sign-in on one of them lifts the authentication reason. An inventory that FilmOpen Cloud refuses for want of a token — the code noToken, with the verdict signIn — is reported as inventoryFailed and makes every later pass of a filmopen target wait for the account’s sign-in: the wait, traced once as reconcile.hold, is the account’s and not a film’s, kept in memory for every film at once. A waiting pass asks the cloud nothing and is traced reconcile.skipped with the code signInNeeded; an ordinary caller gets 0 and a strict one ReconcileIncomplete('inventory', 'signInNeeded'). The wait is read after the target is captured, so a film whose target changed to a folder or Drive is listed and one whose target was removed answers noTarget. SyncService.accountSignedIn() ends it, traced reconcile.release; the application calls it through SyncAppBinding.accountReady() whenever the account reports a user — a sign-in or a restored session, never the saved hint alone — and reconciles the open film with the reason signedIn, logged sync.binding.accountReady; a film closed then reconciles when it opens. A restart starts clear. Drive’s refusal and a Cloud 401 keep the code refused and hold nothing: each pass asks again. The account’s sign-in does not lift a lane’s signInNeeded hold. Permanent target failures become failed. Unexpected failures become sendFailed or receiveFailed for their direction, with retry/dismiss and report the error’s text as attemptFailed. An unavailable binding waits as filmClosed with resume/stop. Binding availability is rechecked around local reads and between stream chunks; closing a film interrupts the send while preserving its checkpoint for reopening. The locally detected closure is retained even if the target translates the stream error. Opening while an interrupted attempt drains also observes its eventual filmClosed result and resumes it while the film is open. restore binds all four durable sync kinds by the film key, resumes that film’s filmClosed jobs and reconciles; a session restore skips the disk journal. jobsFor filters by film and sync kind, views exposes sync jobs, and close closes the owned engine and arrivals stream. SyncService.current starts as NoSyncService until the application installs a service.

The service’s JobReport callback receives inventoryFailed with an empty id and the failure’s text for failed inventory work — a target’s failure reads SyncTargetException: <code> (<verdict>), as every logged sync error does — attemptFailed with the job id and the error’s text for unexpected transfers, and unsafeCheckpoint with the job id and the refused checkpoint’s keys and refusal, never its values, where a credential would be. The text is the error’s as it is, its message and any path in it included; a diagnostic callback that throws cannot change a transfer’s outcome.

The trace. SyncService(trace:) takes a SyncTrace, void Function(String event, Map<String, Object?> data); createApplicationSyncService installs one that writes each event to the log at info as sync.<event>. A trace that throws is ignored. A line carries job ids, lanes, kinds, codes, verdicts, reasons, stages, error types, counts, sizes, attempts and milliseconds; the film’s key as film; a file by its project-relative path, with its area (data, media or root) where it matters; the target by its kind and its identity as it is where a pass starts, a folder’s root beside it; a conflict’s two whole hashes; and a failure’s own text as error, as it is — a target’s failure reads SyncTargetException: <code> (<verdict>). It never carries a token: a request is logged before its token is asked.

Every reconcile line carries film but reconcile.release, which is the account’s. A pass that reaches its film logs reconcile.retire {reason, jobs} (the film’s sync jobs it looks through) before stopping the jobs of any previous target, then reconcile.start {kind, identity, root, reason} (the target’s identity as it is, and for a folder, whose identity is a digest of its root, the root) (open from restore, else the caller’s: change, formLeft, formMoved, writesSettled, released, background, foreground, signedIn, flush, or the service’s own changedDuringSend and fetchDecided), reconcile.inventory {local, target, ms}, reconcile.decided {sends, fetches, conflicts, agreed, deferred, ms} (deferred: the document sends held for an open form) and reconcile.done {queued, ms}; a pass that does nothing logs reconcile.skipped {reason, code: serviceClosed | filmClosed | noTarget | signInNeeded | superseded, ms}, and one that fails reconcile.failed {stage: retire | local | inventory | decide | submit, type, ms} (retire while the jobs of a previous target are stopped) — a refused submission still propagating as before. Each job a pass stops or dismisses there is traced as the action is sent, retire {id, kind, phase, state, action, film, reason: targetChanged}, and as it ends, retire.done {id, action, outcome: accepted | refused, code, ms}. FilmOpen Cloud’s missing token logs reconcile.hold {film, reason: noToken} once, and the account’s sign-in reconcile.release {reason: signIn}, with no film. A pass asked of a closed service logs reconcile.skipped with serviceClosed. The direction is the runner’s, and a count is traced only where it is an integer, since a rejected payload’s may be anything. An attempt logs send.start or fetch.start {id, lane, area, path, size, from, attempt} once its gates pass — a held, closed or retargeted attempt logs none, the last ending .cancelled —, send.checkpoint {id, sent, progress} at each saved progress step, fetch.decided {id, outcome: alreadyAgreed | keptLocal | conflict | receive}, and one terminal line read from its result: .done {id, size, ms}, send.settled {id, reason: alreadyThere, ms}, .cancelled {id, sent | received, ms}, .waiting {id, reason: filmClosed | formOpen | hold, code, ms} (a lane hold, a sign-in’s included), conflict {id, area, path, mine, theirs, ms} (the two whole MD5s), or .failed {id, stage, code, type, verdict, failures, nextMs, ms}. A failure’s stage is where the attempt was: read (the local file), target, write (publishing a received file) or agree (the agreement ledger); a media send’s bytes are read inside the target’s put and count as target, the error’s type telling the two apart. verdict is retry where a retry is scheduled, else the phase, needsAttention or failed; type is present only for an exception; nextMs is the retry delay the result carries, present only where a retry is scheduled. action {id, action, accepted, code, phase} records every action on a sync job — a Stop of a queued or held job, which no runner sees, among them — and conflict.resolved {id, action} an accepted choice on a conflict; hold {lane, code, reason: signIn} and release {lane} mark a lane’s hold changing; restore {film, jobs, waiting, ms} and restore.failed {film, stage, type, ms} end a restore, and restore.skipped {film, code: serviceClosed, ms} one asked of a closed service, which activates nothing. send.woken {film, id, reason: formLeft | restore, ms} marks a formOpen wait resumed by the service itself, send.held {film, id, reason: formOpen} one a restore leaves waiting, and deferral.failed {film, path, type} an application answer that threw, which holds the send. A job’s one follow-up — a document send’s compare (sendCompare), a fetch decision’s pass (fetchDecision), a form’s wake (formOpenWake), a closed attempt’s wake (closedAttemptWake) — waits through one helper for the job to leave working, logged job.settled {film, id, for, phase, code, ms} before it acts, job.settled {…, dropped: serviceClosed | jobGone} where the wait ends without acting, and job.settled {…, failed, type, error} beside the report where the follow-up fails.

Folder sends stage under the captured canonical target root, outside the media and data directories. Redirected staging parents are refused before pathname operations; cleanup removes only the owned part and an empty temporary directory, never unexpected contents recursively. These pathname checks do not isolate the process from a hostile local program with permission to replace directories between a check and a filesystem call.

A document whose form is open is sent when the person leaves it — what they typed; a change that was not typing goes at once (below). SyncService(deferSend:) takes a SyncDeferral, bool Function(String projectKey, String path); null never defers, and an answer that throws holds the send. It is asked of every send in the data lane — the documents and the manifest — and never of media or a fetch; the application’s answer holds only flat data/ documents, so the manifest never waits. At submission a held document is not queued — the question asked at each submission itself, after every earlier one’s wait, a document found held only then logged send.deferred {film, area, path, reason: formOpen, ms} — and reconcile.decided counts what the decision found held as deferred. Before each attempt asks the target, and again immediately before it reads the file’s bytes — whether the job was queued, restored from the journal or retrying after a failure — a held send answers waiting('formOpen') with resume and stop, so no attempt reads bytes that are still being typed. An attempt that answered formOpen after its form was left is resumed once its wait is committed, and restore resumes a persisted formOpen wait whose form is not open — after a crash, none is. reconcile(projectKey, reason:, settle:, strict:) keeps its zero for every ordinary caller; settle: false skips the rules’ settle delay, and strict: true throws ReconcileIncomplete(stage: inventory | superseded, code) where the target could not be listed or a newer pass superseded this one, each coalesced caller getting its own outcome.

createApplicationSyncService answers the question through the container’s SyncAppBinding (syncAppBindingProvider), as documentSendWaits: a flat data/<stem>.json waits while a form is open on that stem in that film (OpenDrafts.of(stem, projectKey:)) unless the binding lifted the deferral for the film, and every document waits while any draft’s write is in flight (OpenDrafts.writesPending), so a send queued behind a form’s last write never reads the bytes from before it. The binding hears OpenDrafts.changes: a form removed (formLeft) or moved to another file (formMoved) in the open film resumes that film’s formOpen waits no form holds and reconciles, logged sync.deferral.lifted {film, reason, resumed, held, ms} (held: the waits an open form or a write in flight still holds; reason is also background and leaving); OpenDrafts.writesSettled, the last tracked write ending, does the same with reason writesSettled, a document whose own form is still open staying held. A film without a target logs sync.wake.skipped {film, reason, code: notSynced | noFilm, ms} and wakes nothing; a wake that fails logs sync.wake.failed {film, reason, error, ms}, a job whose place cannot be read sync.deferral.unplaced {film, id}, and a refused resume sync.deferral.resumeRefused {film, id, code}. A signInNeeded hold is never woken: only formOpen waits are resumed. The app’s lifecycle reaches the binding through the window’s one AppLifecycleListener (lifecycleChanged): leaving the foreground — inactive, hidden or paused, coalesced into one — settles the drafts and, once they are written, lifts the deferral for every film, resumes the open film’s formOpen waits and reconciles (background), so a phone put away sends what its form held; returning (resumed) restores the deferral and reconciles once (foreground, logged sync.deferral.restored {film, reason: foreground, lifted, ms}), so an idle desktop picks up another device’s edit. Leaving starts with sync.background.start {film, state, ms}; a settle that throws logs sync.background.settleFailed {error}, and drafts that could not be written keep the deferral (sync.background.refused {film, code: draftsNotSettled, ms}); a return before the settle ends, or the binding’s close, lifts nothing (sync.background.skipped {code: returned | closed, ms}). There is no polling and no timer. When the preparation ends — settle, lift and reconcile, a refused settle or a pass with nothing to send included — SyncAppBinding.backgroundPrepared, a synchronous Stream<void>, fires only if that preparation is still the current one: its lifecycle ticket current, the binding open and still away. An obsolete or closed preparation does not fire, so a notice never crosses into a later cycle; each end logs sync.background.prepared {outcome: emitted | suppressed, reason: current | superseded | returned | closed, ms}.

A change that was not typing goes at once. A render’s or an import’s insertion, a child added or moved — every change through ProjectController.patchEntity — is sent as soon as it is written, even while its form stays open; through an open form the text written carries what the form held, typed or not. Where the patch wrote its change and a full re-read proved it and still holds exactly the text its own write left, it hands the binding that text (SyncAppBinding.release(projectKey, stem, text, ms:), in memory only), logged sync.deferral.released {film, path, source: patch, ms}, and the binding wakes the film (reason released): a formOpen wait resumes and a reconcile submits the document, which goes in its turn behind any other. deferSend lifts a released document while the open film’s view (§7.5) holds exactly the text released; a write in flight still holds every document, as above. The release ends before every later write of that document through the controller — the form’s save (saveEntity), the patch writer and the epoch declaration on the viewer’s own project file — logged sync.deferral.releaseDropped {film, path, reason: written, ms} (ms: the release’s age), so what the person types afterwards waits for the form again; it ends too where the view’s text differs (textChanged), another film opens (filmChanged), or the film closes or loads or the binding closes (closed). A patch that wrote nothing (unchanged), whose callback did not make its change (rejected), whose write was refused or not proved (refused), whose re-read found a later save in place of its own text — the person typed meanwhile, which waits for the form (overtaken) — or whose film has no target (noTarget) releases nothing, logged sync.deferral.releaseSkipped {film, path, reason, ms}. The patch itself logs entity.patch.start {stem, form}, entity.patch.done {stem, wrote, ms}, a write’s failure entity.patch.failed {stem, stage: write, code, type, ms} and a refused result entity.patch.refused {stem, stage: bind | verify | patch, code, type, ms} — the stem as its digest, never the refusal’s text or stack. A release lost to a restart leaves the document to the ordinary rules.

Leaving a film, in one budget. SyncAppBinding.prepareLeaving(projectKey, within:) settles the drafts, lifts the film’s deferral, resumes its formOpen waits, reconciles at once (flush, settle: false, strict: true) and waits for the film’s document sends — media went when written — until none is queued or working, or within runs out; a send waiting to retry is pending, not waited for, since its retry may be minutes away. Each stage takes what is left of the one budget and checks it, and whether the leaving was undone, before it acts — the resume of each formOpen wait included — so a late settle never lifts; a lift that throws is that stage’s failure, the deferral kept, and an undo during the wait answers incomplete with code: undone. It answers LeavingResult {outcome, stage, sent, pending, code, ms}: complete where every document send is done or known pending in the journal, incomplete where the inventory failed, the pass was superseded, the leaving was undone or the budget ran out before the sends were known, refused where the drafts could not be written. A film without a target ends complete at lift with code: notSynced, nothing lifted; a budget run out at wait ends complete with code: timeout, the sends still under way counted pending. A pending send keeps its journal entry and goes at the next open. undoLeaving(projectKey) restores the deferral (sync.deferral.restored {reason: stayed}); opening another film, closing it and the binding’s own close clear every lift. It begins with sync.leaving.begin {film, withinMs}; each stage logs sync.leaving.start {film, stage, leftMs} and sync.leaving {film, stage, outcome, code, ms}; the end is sync.flush {film, outcome, stage, sent, pending, code, ms}, with sync.leaving.timedOut {film, stage, pending, ms} where the budget ran out. ProjectController calls it on Close project, before another folder or the sample opens (a superseded open undoes it, unless a newer open is already preparing the same leaving), and, for a project created or imported, once the new folder has been read whole and before it replaces the film — each after the drafts were settled and within what remains of kSettleBeforeLeaving (close’s own first settle bounded by it too). A refused leaving stops a switch, a creation’s or an import’s opening as a refused open does: the film and its form stay, the preparation is undone, a folder already created staying on disk; a preparation that throws is undone and logged project.leavingFailed {type}; Close project goes on whatever the leaving answers. The window’s exit (DraftsBeforeExit) prepares the open film first and then runs ExitTasks.runAll — the sync’s own close among them — with what remains of its 10 s — a preparation that throws logged app.exit.failed {stage, type, ms} and the exit going on — logged app.exit.start {stage: prepare, synced} and app.exit {stage: prepare | tasks, outcome, pending, ms}.

Two predicates read the existing jobs and drafts and keep no state of their own, both false outside the binding’s open, ready film with a target: transferring(projectKey) — a sync job queued, working or waiting for a retry (a wait whose code is not formOpen, filmClosed, signInNeeded or paused) — and unsent(projectKey) — transferring, or a job needing attention or waiting for any reason, or a form open on one of the film’s files (OpenDrafts.openIn).

Media on demand. SyncService(mediaOnDemand:) takes a MediaOnDemand answer per film; null fetches every remote-only file as before; an answer that throws fails that reconcile pass at its boundary (reconcile.failed {film, stage: onDemand, type, ms}, a strict caller hearing ReconcileIncomplete) and never chooses eager downloading, and createApplicationSyncService answers true for every film, so on a device that pulled a project the documents arrive at once and the media stay in the cloud until something reads them. Under the answer, SyncDifference.between lists a media/ file the target holds and the device does not as available (AvailableMedia: path and size) instead of fetching it: an available file is never sent, deleted, agreed, journaled or a conflict, and documents, local sends, size mismatches and conflicts decide as before. The service keeps each film’s available paths with the target they were listed at, published in the decision’s order, readable at once through availableIn(projectKey) and replayed to every new listener of availability (SyncAvailability {projectKey, media}) before its changes; a film with no target, or listed at another target, is cleared. A media fetch’s publication invalidates the film’s decision state as a document’s agreement does, so an inventory sampled before it cannot republish the file, and removes the file from the snapshot. fetchNow(projectKey, path, reason:) is the one door: inside the film’s decision order it rechecks the film, its target and the snapshot, joins a fetch job for the path that is not done, failed or cancelled (answering its id), else submits the ordinary media fetch (replace: false) and answers the new id; not open, no target, another target or not available answers null. Lines: reconcile.decided {…, available}, availability {film, count}, fetch.asked {film, reason, path} before the decision order is entered, fetch.submit {film, path, ms} before the submission, fetch.requested {film, reason, area: media, path, size, joined, id, ms}, fetch.refused {film, reason: filmClosed | noTarget | notAvailable | targetChanged, path, ms}, fetch.failed {film, stage: submit, path, type, ms} — each path as it is. NoSyncService has no available media and fetches nothing.

The status, every file against its target. At each pass that decides, beside the available media, the service keeps the film’s SyncStatus {projectKey, at, files} (sync_status.dart): every path of the local and the target inventories that syncs, in path order, each a SyncFile {path, state, progress, code} in the decision’s word — conflict, toSend (here only, or changed here), toFetch (there only, or changed there), available — else, for a document whose two copies differ (a conflict the person set aside), still conflict, and otherwise same; at is the environment’s now. It is cleared, as the available media are, where the film has no target or is listed at another one. statusOf(projectKey) answers it with the film’s jobs laid over it (SyncStatus.withJobs): a file whose transfer is queued or working is moving with its progress, a waiting one waiting, a conflict job conflict and any other failed or attention one problem, each with its job’s state code, one finished since the comparison same; a cancelled one, one already finished when the comparison was made (the status keeps those jobs’ ids) and Keep mine’s (outcome: keptLocal, whose send follows) keep the decision’s word; where a file has several jobs the one moving wins, then a problem, a wait, a finished one. A finished transfer dismissed before the next pass keeps its result in the status (status.settled {film, path, id, reason: dismissed}), so its file does not read as to send again. statusChanges names each film whose status was made, settled or cleared, at once; null and nothing where no pass has compared the film. No call to the target is made for it: it is the inventories the pass already read. Lines: status {film, at, total, counts, files: {path: state}, ms}, status.cleared {film, reason: noTarget | targetChanged}. NoSyncService has compared nothing. filmopen_state’s syncStatusProvider(projectKey) reads statusOf at once, at each statusChanges of that film and at each change of the jobs, equal snapshots suppressed.

filmopen_state’s MediaAvailability is the application’s MediaFetch (§6.9), installed by createApplicationSyncService with its log (media.onDemand.installed {policy}): it answers isRemote from the sync’s retained snapshot (availableIn), so a consumer built at any moment needs no later turn, and hears availability only to tell the widgets what changed. ensure(source, path) decides from the consumer’s own film alone: the file in that source (fileLength) → local at once, whatever the last announcement said; else available in that film’s snapshot → fetchNow, following that job by id — queued, waiting for a retry, working with its progress — until it is done (fetched) or has stopped (failed with its state code); neither local nor available, or a refused fetchNow, is notAvailable. A read begun in a film that is no longer bound is refused, never turned towards the open film. A local file is answered with no line, every open of one passing here; otherwise it logs media.ensure.start {film, path, reason}, media.ensure.decided {film, path, decision: fetch | notAvailable, ms}, media.ensure.step {film, id, phase, code, ms} at each change of phase and media.ensure.done {film, path, outcome, code, id, ms} — a source that cannot be measured is media.ensure.failed {…, stage: local, …} and the code localUnreadable, a fetchNow that throws media.ensure.failed {film, path, stage: request, error, ms} and the code requestFailed — and publishes each file’s MediaFetchState. Previews never download: the media tile (§9.3), the epoch card and the outfit row (epochPictureFor; a take is always local, being found in the device’s own listing) and a mention’s picture (ProjectMentionCandidates) paint the stock picture for a remote file. The upload’s own read of local bytes is the transfer itself and asks for nothing.

A scene’s media before it plays or is exported (§6.14, §19.15). SceneMedia.ready(stem, reason:) (sceneMediaProvider) takes the files the scene’s timeline is made of (SceneTimeline.inputsOf’s present), keeps those not in the media folder that the sync holds on its target (isRemote), and asks for them all at once through ensure — the sync’s media lane runs a few at a time —, saying each one’s end to its caller (done, total); a medium no scene being played uses stays on the target. When every one is here the scene’s timeline is built again at once (sceneTimelineProvider invalidated for that scene, not left to the project’s next re-read), and that timeline is the answer, so that what plays or is exported holds the files just fetched; a failure answers the first failed file and its code, and no timeline. Nothing is fetched where nothing is remote or the seam is not the sync’s: the timeline held is the answer — asked again where the seam is the sync’s, so that a medium another reader made local before the project’s re-read is built in. It logs scene.media.start {scene, reason}, then scene.media.none {why: no_sync | no_project | no_scene | no_store | nothing_remote, files, ms} or scene.media.fetch {film, files, ms}, each file’s scene.media.file {file, outcome, code, ms} (its size is in the sync’s fetch.requested), and scene.media.done {fetched, revision, ms} or scene.media.failed {file, code, failed, ms}; a scene whose inputs or timeline cannot be read logs scene.media.failed {stage: inputs | build, error, ms} and throws.

The transfer host. On Android, uploads and downloads in hand when the person leaves the app go on under a data-sync foreground service until they are done; nothing is shown while the app is in front. filmopen_platform’s BackgroundTransfers (show, update, hide, events) is the seam, in threes: Android answers through the activity’s channel filmopen/sync_host; every other platform has none (supported false, every request HostStatus.notSupported); a refusal or a missing handler answers HostStatus.failed with its code, never a silent no-op. Nothing touches the channel before the first show. filmopen_state’s SyncHost, made in lib/main.dart beside the binding and hearing the lifecycle before it, shows the host at the first departure (inactive, the activity still visible) when the open film is unsent, holds it until backgroundPrepared, then follows the film’s moving jobs — counted as transferring counts them, the mean of their progress, an unknown progress as zero — with an update for a change of count or a step of 5 %, and hides it when nothing is transferring, on resumed, and on the platform’s timeout (Android 15’s daily data-sync allowance). Where no job is counted the notification says FilmOpen is finishing your changes, its bar moving. The texts are resolved from the state’s localisation and travel over the channel. The activity’s answer to show or hide is the request accepted, not its result: the service reports its own start (started {ms}), its failure to start (failed {stage: notification | startForeground, type, message, ms}, the exception’s type and its message, and the service ends), its timeout and every stop with its reason (hidden, timeout, failed, destroyed by the system) and how long it ran; the activity’s destroy reports stopped {reason: activityDestroyed, ms} before it stops the service and clears its reporter, though its engine ending at once may keep that line from the app’s log. Logcat gets the same codes and times, and a failure’s exception message, and is the dependable record of an activity’s destroy. A failed start is not retried until the next departure; a platform without a host logs sync.host.unsupported {platform} once and does nothing else. The trail: sync.host.idle {reason: noFilm | nothingUnsent, state}, sync.host.requested {action: show | update | hide, transferring, unsent, reason: inactive | count | progress | empty | resumed | timeout, files} (a request, not proof), sync.host.accepted {action: show | hide, ms}, sync.host.started {files, ms} and sync.host.stopped {reason, ms} from the service’s reports, sync.host.released {shown, ms}, sync.host.updated {files, progress (a whole percent, a step of 5 compared in whole points), ms}, sync.host.failed {stage, code, message?, ms} — start | update | stop for a request the channel refused (its message on sync.host.callFailed), notification | startForeground for the service’s own failure, with the exception’s message —, sync.host.timeout {files, ms}; never a path or a name, and no text but an exception’s message. The service stops with the activity (its engine is the activity’s), so a swipe-away stops the transfers and the journal resumes them at the next open (§14).

Application ownership. After constructing insertion, lib/main.dart calls createApplicationSyncService, assigns SyncService.current and constructs SyncAppBinding. The journal directory is syncDirectory() where application files are available, otherwise null. Its targetFor callback uses Sharing’s sharingTargetFor(project, auth: container.read(driveAccountProvider).auth, folderPathFor: container.read(settingsProvider.notifier).sharingFolderFor): Drive configuration comes through Sharing, while folder paths are machine preferences keyed by the film. The root directly depends on filmopen_sync for service ownership and reaches Drive only through filmopen_sharing. Construction is inert: it reads no credentials and sends no network request. The binding restores a real film’s jobs and reconciles its changes — a Sharing gesture’s manifest write among them, which the gesture does not reconcile again (§9.4); the web and session copies have no sharing target. A construction failure logs sync.unavailable with the error type alone, and the no-op service remains available. Shutdown runs through the binding’s ExitTasks registration.

6.15 Renders — filmopen_render, filmopen_render_ui

Section titled “6.15 Renders — filmopen_render, filmopen_render_ui”

Settings → Models’ defaults (§9.4). renderPicked reads the preference render.defaults first: the entry for the place’s role at its stage — Draft reading the Final entry — answers with source: settings, its settings the model picker’s row for that model at the stage (Final’s first at Draft), then at Final, Prototype and Draft, else none — the route’s defaults; at Draft the resolution is the smallest the catalogue lists for the model and its variant (renderSmallestResolution: WxH by its pixels, NNNp/NNNP as its height at 16:9, 1K/2K/4K as sides of 1024 per K, a bare number as its side), Draft equalling Final where only one is listed (why: one), where a listed form cannot be placed (unplaced), where the model lists none (notListed) or no catalogue is given (noCatalogue); logged render.picker {kind, stage, source: settings, model, plugin, platform, route, variant?, paramsFrom, params, derived: final?, resolution?, why?, outcome, pickerCode?, ms}. Beside a default the picker is still asked for the place’s kind, and its answer travels with the default’s. renderPickedOpening puts a usable default before the last options, on the remembered place of its route, else its first ready place; a default whose model is not offered, or whose whole route matches no candidate, is not used — render.picker {kind, source: settings, refused: notOffered | routeNotOffered, model, route, then: remembered | picker, pickerModel?, pickerReason?, ms} — and the last options, then the picker’s own answer, decide. The role a default is looked up by: the place’s kind where it has one (an outfit’s reference pictures t2i, or i2i where its words name pictures, as a character’s); at a block’s clip, from what the block gives (renderClipRole: both frames and a voice line flfa2v, both frames flf2v, a frame and a line ia2v, a frame i2v, else t2v), while the offers and the picker keep the clip’s own kind (i2v where a first frame is given, else t2v), the preparation offering the role’s models too, after the kind’s; at a character’s voice samples with no voice kept, ttv: a design never asks the picker, and opens on its first ready route wherever its default is not chosen or does not open here (render.voice.design {…, why, settingsChosen, settingsUsed, ms}). Logged render.kind {place, kind, role, given, reason} in the preparation, and in the dialog for a place with no kind render.kind {place, kind: none, role, why: clip | voiceDesign, given, stage}. The last options are kept whole: the dialog’s render.last entry adds the route as its codec writes it (route, the key included), the variant kept beside it, so that a choice reopens on its own candidate; an entry with no route matches the first candidate of its plug-in and platform. prepareRender takes a stage, asked instead of the version’s own render_stage (render.prepare.stage {source: argument}), and remembered (default true; false reads no last options, render.remembered {outcome: notApplied, why: argument}), both on render.prepare.start {stage?, stageFrom: argument | document, remembered}.

The model picker lists model/platform routes under nonselectable platform headers, ordered by platform name and then model name. Each concrete plug-in and destination retains its identity. Unavailable rows are grey and show their route’s reason. An installed route’s unavailable row links to Settings → Plugins. A catalogue model with no backend candidate remains visible with a missing-plug-in reason; an installed route excluded by the place’s input rules stays excluded. The place’s media-kind filter still applies. Where different offered models share a catalogue name, their menu rows and selected values add a localized input mode: from references for a model accepting reference_images, from a prompt for one that does not. A model with no offered namesake retains its name; route labels and identities remain unchanged.

A render is asked for once and frozen. RenderRequest holds what the person chose when they pressed Render — the model, the route (the plug-in, its platform, the id of the key and the plug-in’s way of running the model), the destination where a route has a choice of servers (RenderDestination, local, server or lent: the application’s name for it and a server provider’s reference, never an address), the prompt composed once, the options by the catalogue’s parameter names (a negative prompt is one of them, negative_prompt, as the interface has no other door for it), the seed, from 0 to 4294967295, the film’s media by input name, the quote (RenderQuote: a cost with its own basis, what one cost buys and the service’s name for the quote, or unknown and no cost at all), the place its outputs go as the insertion writes an InsertionTarget — which always has a project and a stem, and is read here for nothing else — the treatment that applied when the person asked, as the dialog froze it (RenderTreatment, the shape its batch item keeps, Project Specification §8.7, §12.2; §9.3), the stage the dialog chose — starting at the version’s render_stage, prototype where it says none or one of another shape — or, for the render commands, the version’s, the purpose and the origin: local, developer — asked for from the developer page, of the plug-in open there, which the host runs as the page’s working copy only while the page waits for it (§6.11, §9.4a) — or the reserved remote. Its JSON is a job’s payload: no credential, no address, and the key’s id written key_id, which the jobs’ journal does not take for a credential (§6.12); a JSON that is not a whole request reads as none. What it holds is copied and frozen all the way down, so nothing said afterwards — a dialog closed, a setting changed, another film opened — changes it.

An outfit’s outfit_sheets Render door selects the composite in render UI; every other door retains its one-source behavior. The dialog shows the Prompt card on the outfit’s own prompts.sheet — the prompt its page’s card edits, writers’ target outfit-sheet, its @ offering the outfit’s inspirations and references — bound to the outfit’s open form’s draft where one is open, else read-only. The outfit’s own words carry the sheet (“this character, as shown, wearing this outfit”): the character’s words are neither sent nor read for tokens. The character is frozen as the dialog opens: its open form’s draft, including typing, else the indexed file, at the exact filename epoch — the SHA-256 of its complete canonical JSON document and its live references. The outfit is taken at Render, from the one document the card shows (the open draft as it stands, else the file as the dialog opened on it): its words, the media they name and the SHA-256 of that document, so typing or Generate in the card never refuses its own render. The negative is the outfit’s own; the existing visual treatment is applied once after the prompt. Submission rechecks the project opening, viewer, fresh exact-epoch parent pick and both hashes — the character’s against the film now, the outfit’s captured document against its draft or file now — before returning a request, and logs render.outfit.sources {captured: opening|render, parent: frozen, outcome: ok|refused, reason?: project|closing|viewer|parentPick|missing|changed, ms} at the character’s capture and at that check (the source documents and references logged whole). It refuses when the captured target’s project key differs from the sources’ key (project), and as soon as the captured project is no longer the open one — another requested, including while the opening waits for drafts to settle, closed or replaced (closing). A changed binding or source refuses inline with the existing stale action sentence; an outfit file changed on disk with no form open is refused so, never sent with its old words under a new hash; an initially unresolved parent refuses with the existing unresolved sentence. The final prompt, including the one treatment application, and the snapshots are frozen in the request. A take continued at an outfit’s sheet keeps its box and sends its words whole, as recorded or edited; its sources are checked as a new render’s.

Besides the pictures a prompt names (§9.8) and a continued take’s, only this composite context makes reference_images an available input for model eligibility. It is one ordered list, each picture once: the readable live entries of the character’s reference group, frozen as the dialog opens, first; then the pictures the outfit’s words name, or, where they name none, the outfit’s own live references, read at Render — each group’s order kept, archived and deleted entries excluded, with kept and unreviewed entries both usable. A model accepting that input receives the list actually sent, and the dialog refuses inline below its declared min or above its max of that list, stating the count without truncation or a new selection control, logged render.outfit.bound {outcome: ok|refused, reason?: referencesBelow|referencesAbove, count, min?, max?, references, ms}, decided once each time the model or the pictures change, never per frame; render.outfit.inputs {from: named|composite|none, character, outfit, sent, words: own, bound, references, entries: [{reference, unreviewed, favorite}], skipped: [{reference, reason}], ms} (the pictures sent; a line is said again where they change, at the same count too) says which, with each part’s count, where it changes. A model accepting no references receives none (from: none), and the dialog says it renders from the prompt alone. The outfit’s tokens resolve in the outfit (renderOutfitPromptInputs), against its outfit-sheet candidates, and are written in the row’s placeholders by their number in that one list — after the character’s pictures, so a parent portrait and the outfit’s @reference-1 send the portrait first and write the token as the second picture; the whole list is held to the model’s bound, never cut (references-count), and a token naming nothing refuses tokens-missing, as every prompt’s does. The kind at an outfit’s sheets is i2i where the outfit’s words name a picture, both modes where they name none, and follows the card’s words as they change (§9.8).

Source composition freezes provenance. SourceSnapshot derives its entity identity, exact filename epoch and picked version number from the complete picked version stem. A path, extension or pointer is not that stem. Each primary or dependency snapshot retains a caller-supplied ContentHash with its algorithm named; the render package computes no digest and performs no source resolution while constructing a snapshot. RenderComposition freezes the dependency list, prompt and structured reference strings and maps in the same recursive JSON shape as RenderRequest.inputs. The source passed to the request remains the complete primary picked version stem, not its logical entity identity. Both source hashes and the selected references remain captured when the render is submitted.

A one-source request needs no composer: its prompt, source and inputs go into the request as they are. The UI consumer belongs in filmopen_render_ui. OutfitRenderComposer resolves the parent character by removing the outfit tag’s last dotted segment (Project Specification §9.2), reading only the character group’s exact filename epoch and using FilmProject.pickAt for the viewer. Missing or ambiguous parents remain unresolved, with no epoch fallback or index mutation. It joins the character’s positive prompt it is given before the outfit’s with a blank line, omitting an empty side — the dialog gives it none, the outfit’s words carrying the sheet —, and preserves the character references before the outfit references without truncation, the dialog giving as the outfit’s only those the character does not hold already. Its dependency must name that exact-epoch parent. State, forms, browser and tree do not reach render through a resolver. Optional request provenance is primary_snapshot and source_dependencies, each snapshot holding stem and content_hash with its named algorithm and value. The primary stem equals the request’s source; dependencies require a primary. The existing job and journal codecs retain these fields at submission and restore, refuse malformed provenance, and omit it entirely from ordinary one-source requests.

One queue, which needs no film. RenderManager holds every render: submit never refuses because a queue is busy, and answers the render’s id — the jobs engine’s own where the manager runs on it, else a random UUID (newRenderJobId) — which ctx.job.id carries, in the form both the jobs’ journal takes for a job’s id and the insertion keeps as a producer’s recovery (§6.13): 8 to 96 lowercase hexadecimal digits and dashes, the first a digit (isRenderJobId); a queue per RenderResource — a server or a key’s account, named by its platform and a digest of what stands behind it and never by an address, which the backend says for each request — runs as many as its capacity, the person’s number, else its platform plug-in’s, else 1 on the person’s own machine and 2 on a key, a number below 1 being none (RenderCapacity), never more than three in all; stop keeps a queued render from starting, ends a call in flight, asks the service to stop work it holds, collecting what it had finished, and gives up staged outputs nobody took in, letting them go while what they cost stays recorded — each then cancelled; pokeNow(id) asks about held work at once instead of at its next poll — a lending notice said the job changed (§6.6c) — cancelling the poll’s timer and sharing a question already in flight; held as restore reads it, so ready outputs and a render waiting for a person ask nothing, nor does an unknown job; its decision said before it waits, render.poke.start {found, branch: collect|shared|skipped}, and its end by render.poke {found, inFlight, reason: collected|shared|notHeld|unknown, ms}; restore keeps a queued render queued, asks again about a pending one, makes one whose outputs were staged ready again and marks a single call cut off by a closing application interrupted, sent nowhere, and the application runs it before the insertion’s own, so that a producer’s run finds its job; nothing is sent twice by itself. Its refusals are RenderRefused codes. An incomplete journal reading refuses new managed submissions as journal, preserving work whose state or resource this build could not read. RenderManager.current is NoRenderManager until the application installs one.

The manager the application runs, BackendRenderManager, over a backend and a RenderJournal: every render in the order asked, each resource as many at a time as its capacity and three in all; its id a newRenderJobId; its intent written before its call — the journal holds submitting before anything is sent, so a restart that finds it there calls it interrupted — and nothing sent where the intent was not written, the render then failed with the code journal, nor where a stop or a close came while it was; the whole document after every change, the renders not over and the newest hundred completed results, in memory as in the file (RenderJob.toJson, read back by fromJson); a private durable terminalHistory orders retention by completion, separately from public reverse submission order. A newly acknowledged older result therefore remains confirmable through unrelated saves and restart until later completions age it out. Legacy journals seed retention from their existing order; changing the retained limit applies a smaller bound without treating a valid older larger history as corruption. Unreadable or contradictory retention ranks are discarded while valid known terminal ranks and the ordinary fallback order are retained; work a service holds asked about again at its plug-in’s hint, kept between 1 and 60 seconds, one question at a time, holding its slot meanwhile; acknowledgement and Stop persist their disposition before letting anything go; a stop acts on what the call it waited for answered — work a service now holds is cancelled by its new receipt, work that is over, made or failed — a render whose service had finished and whose downloads the stop cut among it (RenderFailed cancelled at the stage downloading) — is not cancelled at all, and nothing is fetched again. An end is written twice: its prepared payload before settlement, and its applied end, the candidate still in the journal, after it, before publication or releasing capacity; a refused write keeps the candidate and resource in settling with Retry alone. The next write, or a restore, prunes the ends retention no longer keeps. Retry repeats persistence, without repeating Usage application. An applied durable terminal at restore remains finished without another effect or execution holding. Final publication uses its already-staged completion order rather than refreshing an entry aged out during delayed cleanup. An unconfirmed stop stays in stopUnconfirmed. An ordinary collection that fails without a confirmed terminal provider answer stays in collectionUnconfirmed. These are nonterminal jobs needing attention and keep their resource and receipt through restart. Retrying asks about the existing work, without submitting it again; Dismiss gives the job up without another provider call. Stop journals an accepted receipt and unconfirmed intent before signaling or waiting for an active call and before cancellation, including a renewed receipt. If that intent cannot be saved, no cancel is sent. Direct Stop is tracked through shutdown, and even an expired shutdown grace leaves its intent held across restart. Other attention actions refuse while cancellation is active. A confirmed cancellation or a later collection with a terminal provider answer releases the resource. A cancel answered finished or unknown — the latter from a provider whose cancel only asks, its next collect telling how the work ended — is followed within the Stop by one collect of that receipt: a success (its outputs let go) or a confirmed failure ends the job cancelled, and a pending reply or an unconfirmed failure stays stopUnconfirmed with the newest receipt, for Retry or Dismiss; there is no loop and no timer. A job on FilmOpen credit the person stopped is confirmed by its operations’ End and their receipts (§6.6): every operation settled, the call the Stop cut short or the cancel ends the job cancelled at the receipt’s cost, whatever the plug-in answered, with no further cancel or collection; an operation with no receipt confirms nothing. The manager’s optional events callback is told each step of a Stop as render.stop — requested, refused with its reason (closed, stopping, acknowledging, unknown, over, finishing, settling, stopUnwritten), accepted, cancelAnswered with the cancel’s cancelAnswer, the decision (stopped, collect) and its reason (confirmedByCancel, collectToLearnEnd), collectAnswered with the cancelAnswer, the collect’s collectAnswer (the answer as render.collected logs it: its state and the code, stage, confirmed, charge and message of a failure, the outputs of a success, or the receipt, phase, progress, wait and charge of a pending reply), the decision (stopped, stopUnconfirmed) and its reason (finishedOutputsLetGo, confirmedEnd, unconfirmedFailure, stillRunning), then completed or failed — with the job, its phase and the milliseconds; a cancel, a collect or the letting go of what it made that throws is render.cancelFailed with the job, its stage (cancel, collect or letGo), the error’s text and type, the decision stopUnconfirmed, the outcome failed and the milliseconds. A restore is told as render.restore.start, render.restore.read (whether the journal was read, each restored job’s phase, how many entries were dropped unread, the milliseconds), render.restore.job per job (its saved and restored phase, the decision — asSaved, pendingEnd, preparedSettlement or the code it was given — its resource and receipt) and render.restore.done; an entry dropped unread is told under its code (render.jobUnread, render.pendingEndUnread, render.acknowledgementUnread) with its saved id and phase, the whole entry, the decision dropped and the reason (unknownPhase, notAnObject, malformed, duplicateId, …). Every other operation is told too, each with its milliseconds: render.submit.start (the request) and render.submit (the job, its resource and capacity, whether written, or the refusal); render.admit for each queued render the queue considers (held, capacity, holding, admitted or waiting with its reason: ceiling, unclaimed, stopped, finishing, capacity); render.send (sending once its intent is written, or why not) and render.sent, render.collect (asking with the receipt, or skipped with why) and render.collected, each with the answer, its receipt, code, stage and charge; render.settle (ask again with its wait and why, attention, or given up); render.keep (the facts kept and whether written); render.finish (start, preparedWritten, settled, done, or refused or failed with why; each with the settlement payload it carries, a restored one’s included, and its milliseconds — the host’s replay of that payload logs usage.settling with its lines, and usage.settled with each record’s id, amount and chain as appended or skipped, a skipped one with its saved and logged lines and whether they match, §6.11); render.acknowledge; render.act; render.release; render.save (how many entries, each live one’s phase, or failed); render.close. The manager’s report is given the error itself, and startRenders logs a report with the error’s type and its message. startRenders writes them to the log, a refusal, a failure or an entry dropped unread as a warning. Stop honors a confirmed terminal answer from the active call it waits for without cancelling its old receipt again, and preserves any charge that answer reports. A canonical pending reply resumes ordinary observation with its resource held. The unconfirmed-end marker survives this resumption and restart, so an unanswered later collection returns to the corresponding attention state. A Retry that times out or fails without that answer keeps the attention state, receipt and resource; RenderFailed.confirmed distinguishes a canonical terminal answer from transport failure. A binding refusal before submission has no receipt and ends as failed, releasing normally. RenderAttentionManager exposes generic job views and actions. The application composes them with insertion views using only the insertion-owned validated render id. Tile and Activity preserve insertion identity, placement, progress and presentation while showing that exact render’s attention sentence and offered actions. Actions are rechecked and routed to RenderAttentionManager.act: Retry collects, replays settlement or acknowledges without submitting; Dismiss gives up that exact hold. An acknowledgement-only insertion subsequently advances its saved marker through its offered ordinary Retry. A true insertion identity refusal still forbids mutation; restoring a server setting alone does not change that identity. When restoration completes, the render manager publishes its loaded snapshot through the existing change stream. Subscribers established before reading see recovery actions without another job event. Closing suppresses a late restoration announcement. After Retry collects, a refused insertion offering Dismiss alone retains its refusal phase and meaning while its exact render still offers Stop; the consumer offers that Stop instead of local dismissal. A Stop-only binding refusal uses the existing action-neutral insertion sentence about the changed film or settings. A stale Dismiss is refused without removing the insertion. Unconfirmed Stop and cleanup keep the manager’s offered recovery controls; ordinary insertion dismissal returns after terminal release. Retry and Dismiss reach the insertion too where the insertion itself waits stopUnconfirmed and offers the action: Retry, where the manager offers it, once the manager has taken it — where it offers none, straight to the insertion — a Retry the manager offers while the insertion’s attempt runs staying the manager’s; Dismiss goes first to the manager — through the manager’s Stop where the render offers no attention, having reached outputReady after a refused Stop, which gives its staged outputs up — and to the insertion only once the manager’s state, read again, shows the render over; a render not over — left settling, say — keeps the insertion and its controls, and the action is refused with the render’s phase as its code. Each tap logs insertion.attention.route {id, renderId, action, stateCode, managerAccepted, renderPhase, forwarded, insertionAnswer, insertionAccepted, reason, ms}, and a tap that throws logs it at warning as {id, action, reason: failed, error, ms} before the throw reaches the tile. HTTP collection errors retain the accepted receipt, resource and open Usage for collection-only Retry. Dismiss releases the local hold without deleting its uncertain dispatch fence.

An optional RenderSettlementBackend registers managed jobs and prepares their terminal effects in a nonempty payload, even where there is no effect. An empty prepared payload is refused. A candidate still preparing effects is excluded from terminal journal projection. While preparation is incomplete, the journal retains the original terminal disposition in a private pendingEnd envelope inside a nonterminal settling entry. Restore validates its id, frozen request, resource and terminal phase, retains its output references, and lets Retry prepare that same disposition without a provider call. An entry whose envelope this build cannot read is logged whole and dropped, as the next paragraph says of every unread entry. The manager saves the prospective terminal state and opaque effect payload before applying the effects, publishing outputs or releasing capacity. A failed save or effect leaves settling, needing attention with Retry alone and retaining its outputs and resource. After applying the effects, the manager saves settlementApplied before publishing the job or releasing its resource. A failed marker save retains settling and its prepared payload. Restore replays only unapplied terminal effects before publishing the job, without asking the provider again. An applied terminal or output-ready entry never replays or holds capacity at restore. Unfinished effect payloads are retained even beyond the terminal-history limit. An optional RenderFactsBackend is given a keeper (attachKeeper) through which it hands the facts of a render — its request and its outputs — to be kept on the job as RenderJob.facts, stored as given and present at every phase the keeper wrote, a failure or a cancellation with no output included. The host’s backend is one: it awaits its keeper before each next step and hands it every fact known so far — prepared, before the call that prepares and sends, from the frozen request alone (no plug-in stem, no drawn seed); answered, once the containers are kept and before anything is fetched, with outputs as the plug-in said them and no delivered, a failed answer’s error with it; once per output handled, fetched or missed, with delivered so far and the output staged where it was fetched, its record provisional until the outcome’s list replaces it; and on any answered cancel, with the cancel’s container. A pending answer is not kept: the container is complete at every answer, and the terminal one holds it all. Anything a keep throws ends the job there as journal, charged where a service was reached, never retried; a cancel’s throw is the manager’s to hear. The manager attaches its keeper when it is made. Each call of keep replaces the job’s facts whole — the backend’s container is complete at every call, so nothing is merged — publishes the job, and returns only once the journal holds them, so that the backend’s next step — sending, downloading, the next download — follows a durable keep. An output the backend staged with its facts is appended to the job’s outputs in the same journal write, so that a staged output is never journaled without the facts that say what it is, nor the reverse. A write the journal refuses makes keep throw RenderRefused with the code journal; the backend answers its call RenderFailed('journal') and goes no further. On a first call that ends the render failed with the code journal; on a collect, where the service holds the work, it is an ordinary unconfirmed collection, collectionUnconfirmed with Retry, so that work a service holds is never given up for a local write that failed. A keep for a render the manager does not hold does nothing. A render that ends failed or cancelled after a staged output lets that output go, as a stop does: nothing takes in what a failed render staged; one left unconfirmed keeps it for Retry. The settlement payload the backend prepares carries whatever the render’s end says of its time and cost; the manager keeps it as it came, prepares it once and applies it once across restarts, and computes none of it.

An individual entry this build cannot read — a job an earlier build saved in a phase this one has not, bindingChanged among them — is logged whole and dropped: it has no view, nothing manages, collects, cancels, settles or releases it, its staged references are not held, and the next write leaves it out, as it leaves out envelope fields this build does not know. Only an unreadable whole journal envelope, including an unsupported version or non-list jobs field, sets restoredWhole false and refuses new work; the original file is then untouched.

A plug-in-thrown provider error is not a transport miss and waits for the person’s Retry. Unanswered timeout, connection and network operations alone receive the three automatic waits of one, two and four seconds; request timeouts are additional to this seven-second delay budget. Once that budget is exhausted, only Retry asks again until a canonical pending answer resets it. Stop on a paid request waits for its in-flight fate under that request’s own timeout before terminal settlement is copied.

Acknowledgement confirms an already-durable done result again without another effective release or terminal Usage record. Concurrent acknowledgements share the same result. Success follows a durable done intent and durable cleanup completion; a refused required journal write throws RenderRefused of journal, while a failed staged cleanup throws RenderRefused of cleanup. A private pendingAcknowledgement record retains the original outputs across failure, restart and terminal retention until cleanup completes durably. Its staged references remain held against sweeps and own no execution resource. The optional render attention view exposes acknowledgementPending with Retry alone for every unfinished acknowledgement, including a refused intent write, failed cleanup or refused completion marker. Retry resumes this cleanup without rendering, collecting or repeating Usage settlement. An incomplete acknowledgement envelope is logged whole and its entry dropped, its staged references no longer held; readable neighboring jobs continue. Closing waits acknowledgement operations within its normal grace and saves their recovery checkpoints even when their caller receives a typed failure.

Stop marks a queued render as settling synchronously before any persistence wait; neither the pump nor the sender can start marked work. An ordinary poll whose transport fails without a confirmed provider state gets three automatic retries after one, two and four seconds. The consecutive failure count is kept privately in the journal and is bounded across restart and manual retries. A canonical pending answer resets that count; a canonical terminal answer ends the work at any point and clears an earlier uncertainty marker. After the automatic budget is exhausted the resource stays held as collectionUnconfirmed, for Retry or Dismiss. Binding and unconfirmed-Stop attention retain their own rules.

The manager counts the scheduling resource it computes from each request, independently of the resource label recorded in the journal. Restore recomputes the resource under the current backend rule and retains the recorded label for display. Every restored pending job takes a holding before fresh work is admitted, including holdings over that resource’s capacity; no fresh job starts there until those holdings leave room. restore runs once: it reads the journal after the application’s work before reading (beforeReading), and still reads it where that work failed; until it has, nothing is written over it, a close included — a render asked for, or a producer’s run restored, meanwhile waits for the read (whenRead), so nothing is written over what the journal holds — runs nothing until the application’s work before running is done (beforeRunning), and keeps a restored queued render waiting until its producer watches it again, so that one whose producer’s recovery was never saved is never paid for with nobody to take it in; one nobody followed for a week (unclaimedFor) is given up, cancelled with the code unclaimed, never sent. What the journal holds that this build cannot read is logged and dropped. A journal of a version other than the current one (renderJournalVersion, 2) — older, newer or with none — is set aside unread through the journal’s own door (setAside: the host’s file renamed to <name>.old, byte for byte, in place of any earlier one), and a fresh journal is started: nothing of another build’s renders is carried, and the manager reports render.journalSetAside. A journal whose whole file or envelope cannot be read is left as it was; nothing it may name is swept (restoredWhole), no new submission is admitted, and no save claims a write it did not make. The journal is MemoryRenderJournal in a test and where the application keeps no files, and the host’s file, written whole through a temporary file and a rename, beside the jobs’ journal (§6.11). The application starts it with the host’s startRenders before the insertion is made, and registers ManagedRenderProducer with the insertion service under renderProducerKind.

Its one door, RenderBackend: candidates (who can run a model here, each with its readiness — a RenderNotReady code where it cannot, serverStarting for a server still starting, and, where the reason is the platform plug-in the route runs through — its state, a key it may not use yet, or its absence — that plug-in’s name (through, its tag where it is not installed), and where no key is saved the plug-in whose own key it is (keyOf) — and its resource, a route that can run in several places — the person’s own ComfyUI, a server a provider started — being one candidate for each, told apart by its destination: the host’s backend adds, to a route on a platform a server’s engine is, one candidate for each server the sessions hold starting or running with that engine (ServerSessions.runningEngine; a session takes its engine from its provider’s status, else from the one engine the provider declares), the oldest first, queued apart on the queue a render there goes on and billed by the server’s time; a ready server the person started here first (one found at the provider and not started here is not), since its minutes are paid while it waits, then the person’s own computer, then the other servers and lent offers, then what bills by the call — render.candidates logging each one’s name as found and as shown, its rank and why —, and two named alike each said with its plug-in’s name, or a number where that is not enough), resourceOf (the queue a request goes on), options (a route’s ways of running a model, each with its readiness and the plug-in’s own words for what stands in its way, their choices, what they need of the machine and the price of the choices asked about — the interface’s options — for a route that answers none, one way of running the model, its plug-in’s own default, as ready as its platform plug-in’s status and then the route’s own plug-in’s say where the render would run, a server’s through its endpoint; a bounded inspection that is busy keeps its last completed choices and returns RenderOptions.busy with a finite UTC retryAt, for a fresh ask, retaining known readiness; without completed choices it supplies one not-ready stack and no quote), render and collect, each bound to its job and ended when the future it is given completes, cancel, which answers what the service says — cancelled, finished or unknown — and, for what a render made, item and release. A render succeeds with RenderOutputs, the backend’s own references to bytes it staged with the stack that made them and the provenance their takes are recorded with, never an address — the manager keeps them with the job, so a restart changes none of it; stays pending with the backend’s receipt — the plug-in’s, and what the backend keeps with it — and its phase at the service; or fails with its code and stage, what it cost already in the usage log (§6.7), and whether a paid service took one of its requests (RenderFailed.charged: the host’s usage ledger saw a request on a person’s key answered with a 2xx, or sent and never answered), which the manager keeps with the job (RenderJob.charged, in the journal as charged). The plug-in-backed backend is the host’s (§6.11).

The servers a person starts, ServerSessions (server_sessions.dart), kept apart from any dialog over a ServerBackend — the host’s in the application, over the server providers’ door (§6.11) — and a journal of their own, servers.journal.json beside the renders’ (§6.11): a catalogue (quote, serverOffers with no arguments) of every GPU class, VRAM, priority and whole hourly price, with availability unchecked. Selecting an offer checks only its GPU class (checkOffer, gpuClass), matching its id and priority; unknown or zero capacity refuses deployment, and a changed selection invalidates the check. Each explicit Deploy rechecks that selection before starting one server, using the held checked offer’s metadata and price. Malformed answers fail as a typed refusal; blank provider words leave its code to be said in the application’s language. Discovery shares one thirty-second admission-inclusive deadline across capabilities and catalogue; each selected check or recheck starts a fresh deadline before it asks. Closing the window or replacing a gesture cancels that operation. The host keeps both for each call: a spent instant or a cancellation refuses the next call before admission, a held provider is answered timeout by the instant, and a cancellation cancelled once observed (servers.quote and servers.quoted log each catalogue and check, with the time it took). Scoped capabilities timeout or cancellation is terminal and never falls through into another catalogue call. A host timeout says that the provider did not answer in time, and a cancellation says stopped; other host failures keep their own sentences. Unchecked, unknown or unavailable capacity and exhausted or unreadable quota each have their own refusal sentence. Only the current generation may clear its busy state, and an obsolete or expired recheck sends no deployment. The host closes requests by the operation end and answers terminally while a durable Usage append may keep admission until written; a handed-off deployment retains its existing session owner. Prices hold five minutes (ServerQuote.holds). A present raw quota reads container_groups_quotas.container_replicas_quota and container_replicas_used: both finite non-negative numbers are known, use at or above quota is exhausted, and null or an unreadable shape is malformed. Exhausted or malformed quota refuses deployment; absent quota leaves the provider’s gate at deploy in force, said in the panel. The panel retains a catalogue quota refusal until Refresh replaces that catalogue; a selected reply cannot erase it. A deployment (deploy) of one checked offer has a lease of inactivity minutes the provider allows (serverCapabilities: defaults 15 to start at, 5 to 60, or its declared range), under a name the application makes (filmopen- and ten lowercase letters and digits) and keeps in the journal before the provider is asked — a name that cannot be kept asks for nothing — and never asked for twice: an answer that is not a server, or none, is looked for by that name in the provider’s list (listServers) — a name the list lacks made nothing (failed, nothing to stop) at once where the provider refused the deployment, and once three lists lack it where it gave no answer, and a server listed after all is followed again with its stop — and the provider’s reason stays with the session until the server runs or an action settles it; a quote older than five minutes, a lease the provider does not allow, an offer not quoted, a provider turned off and a second start of the provider’s while one is asked are refused before anything is asked (ServerSessionRefused); the lease renewed (wakeServer) once less than half of it is left, while a job holds it (hold: the render manager holds it for a render on the server from its sending to its outcome, between the questions asked of it too, and the host for each call it makes there) or while the server starts, for at most ninety minutes after it was asked for (startupCap, past the longest cold start a provider lists and equal to the SaladCloud container’s own boot bound, which the container counts from its own start, after allocation and the image pull — so a start whose allocation and pull take long can still lose its renewals before the container gives up; where it stops renewing, servers.startupCap says so once), and never while nothing holds it — an open dialog keeps no server awake; a stop (stop) says stopping until the provider says the server stopped, a list read meanwhile leaving it asked, a stop asked again where one is on its way adding nothing, a stop the provider took and has not carried out — the server still held running, an instance counted — asked again at most every five minutes (servers.stopAgain), never while the provider says it stops, which a stop asked again would start over, and a server asked to stop that its provider lists no more is stopped; a server that was made and that its provider no longer lists is unsure (uncertain with its server known, ServerSession.unsure, notListed), never taken as stopped: no render is sent to it and nothing renews it, its stop is asked by its name, it is followed again once listed, and it is expired, ended at its lease’s deadline, once a list its provider answers past that deadline still lacks it, its doubt then settled and not said again; its provider is asked about it at most every five minutes (ServerSessions.unsureEvery) rather than at every look, each list being a request on the person’s key; the clock alone ends no server: past its deadline (ServerSession.deadline: expiresAt, else askedAt plus its idle minutes) a server whose end is unconfirmed (ServerSessions.unconfirmed: its provider’s plug-in off, providerOff; its provider’s last list in this run not answered, refused or unreadable, unanswered, a restored server until its provider’s first answer; its provider still saying it stops, stillStopping) keeps its phase, its stop and its row, its running time counts no later than the deadline or than when it was last seen running past that (ServerSessions.runningFor), and the end its provider then answers — stopped, or a list without it — is capped the same way wherever the server was unconfirmed as the answer came, a list asked before the deadline and a Stop pressed past it included; its provider is asked again at start, at its looks and when its plug-in is turned on (servers.refresh with its reason, pluginOn, what its list said before then holding no more, servers.listForgotten; servers.refreshed with the outcome and each listed server’s reference, label, engine, stack, state, instances and expiry, or a failure’s stage, type and words); logged as servers.estimateCapped, servers.unconfirmed, servers.confirmed, each phase servers.adopted and each end servers.ended, why then, and each stop the person asks as servers.stopAsked (the reference sent, whether unconfirmed), servers.stopAnswered (the answer whole), servers.stopFailed, servers.stopNotAsked (its refusal) or servers.stopSkipped; a lease that passed with no instance running is expired, whatever the provider calls the server’s state, and ended at its deadline however long after the application noticed — or, where it was seen running past its deadline, when it was last seen running (ServerSession.seenRunningAt, kept in the journal), and never before it began to run — and a server runs by the count of its instances where its provider counts them, else by its word running; but where the provider still holds it — starting, running with no instance, or in a word the application does not know: a start not called off, an instance being placed again — its stop is owed (ServerSession.stopOwed): asked at once (servers.stopOwed), and again at most every five minutes while the provider holds it so, never while it says it stops, until it holds it stopped or lists it no more, a stop it refuses said by its code (servers.stopRefused, a provider’s code logged only where it is one, and kept on its row until the stop is owed no more); the server stays listed with Stop, which asks now, or says that it cannot where the provider’s plug-in is turned off; and one expired that its provider then counts an instance of is followed again as a server past its lease (servers.runsPastLease), its usage’s end written again when it ends — what it ran before its lease ran out and since, never the time with no instance — the later record counting (§6.7); a server its provider still runs past its lease — the provider’s own schedule late — is said to be so (ServerSessions.overran), is offered for no render — nor is a server whose provider’s plug-in is off, which nothing could keep awake — and, where no job holds it, has its stop asked by the application once the deadline is two minutes past (leaseGrace), and again at most every five minutes while its provider still runs it (servers.leaseOverrun) — a stop refused before anything is sent, the provider turned off, at the same pace (servers.stopRefused), its row then saying so until the provider is on again, when its stop is asked at once — a server found at its provider included, its lease, whichever FilmOpen set it, having passed with nothing renewing it: the lease is the person’s word, kept wherever the application is open to keep it, what each provider is called kept in the journal for a provider turned off by the next run, a start keeping the newest hundred finished sessions (ServerSessions.keptFinished; servers.prune with the sessions held and the bound, servers.pruned with what it kept and dropped, why and its milliseconds, whether or not anything is dropped) and every one that still owes a stop whatever its age, each pace counted while it runs, a restart asking at its first look; the servers that start or stop looked at every half-minute (tick, check), a server past its lease every half-minute until its stop is asked and then as often as it is asked again, and an unsure one, one whose stop is owed and one a job holds past its lease at most every five minutes, whether or not its provider answers; a start supervised only where this application asked for it and nothing was refused since; what could not be kept — the journal, a usage record — and what was concluded from a list reported to the log (report); and, after a restart (restore), the sessions read back, a deployment that was being asked looked for by its name, a server the provider lists and no session holds found (found) with its stop, and nothing started; each provider’s list is asked again when its plug-in is turned on (pluginOn) and on each arrival on the GPU page (gpu-page, §9.7a), never on a timer. A server running is not a server ready: whether its engine answers is its platform plug-in’s to say. A compute session’s usage (ServerUsage) is a chain of its own (§6.7): started with its lease’s estimate at the quoted price once the provider answered with a server, and succeeded with its running minutes and their estimate, its cost unknown, once it stopped or its lease ran out — an estimate, never the provider’s bill, which no request to its management is. ServerSessions.current is the application’s (the host’s startServers), null where none runs.

Where it meets the insertion, RenderProducer, a producer of kind render on the jobs’ contract (§6.12), and ManagedRenderProducer, the one over the installed manager: Render hands the place’s service a ProducerSource('render', request); the insertion starts the producer with the request; the producer submits it, relays the manager’s watch as states named by the render’s phase, and at outputReady answers the manager’s items — the staged outputs, opened by the backend, each with its provenance: the run that made it and the record of what it was asked — which the insertion takes in at the captured place as it takes in an upload, as the run’s takes (§6.13), then acknowledges them, so the render is done and its staged bytes let go; a failed, cancelled or interrupted render ends the run with its code — marked charged.<code> where a paid service took one of its requests (the format’s chargedFailure; a stop is never marked), so that its tile says it may have been charged — which the insertion keeps as the producer’s own — producer.<code> (producerFailureCode, the code as it came; a stop stays the job’s cancelled), so that a page tells it from the insertion’s own codes after the render (producerFailureOf, empty for a failure the job kept no code of, producerFailed). Where the provider said why — the message the host cut to its bound and keeps in the job’s facts under request.error.message — the run’s ProducerFailed carries it as its detail, read from that one member and nothing else; the insertion keeps it on the attempt’s binding and writes it, once, as the state argument detail of the checkpoint before the job’s terminal row, which keeps it, so that the queue’s row and the place’s tile read it as what happened, in the code’s sentence’s place and after any possible charge (§6.7), after a reload too; where the provider said nothing, the code’s sentence stays. It is logged with that checkpoint’s arguments (insertion.journal.write), and never enters the job’s payload. A refusal of the manager’s at start — noManager, or journal where the manager could not read its journal — and a restore onto a render the manager does not hold — noJob — reach the insertion as JobUnavailable of that code, from which it makes the job’s row, which says it in a sentence of its own; notReady and cleanup, the manager’s refusals at items and at the acknowledgement, have sentences too, though the producer does not hand them on as codes; the host’s journal, reaching the row as producer.journal, reads the same sentence as the manager’s; a refused restore is logged insertion.producerUnavailable with its code alone; a request that is no request stays invalidPayload. The run’s saved JSON is its job’s id under id alone (renderRunJobKey), which the insertion keeps and restore attaches to again. Neither package imports the other.

The pending batch’s Render, each item once (RenderQueue, filmopen_render_ui; Project Specification §12.7). One pass at a time per queue in an app: a second press while one runs does nothing (queue.submit.busy). While the plug-ins’ settings are read again (PluginSettingsControl.current.machineSettingsSnapshot null) the pass does not start: nothing is written, every item stays pending, it answers not-ready and logs queue.pass.notReady {stem, film, reason, ms}, and the person presses Render again. A pass takes a pending record with an item to submit, writes it submitting, then takes in order each item with none of job, submitting and error: an item the codec cannot read refused with its code (queue.submit.unreadable), then the person’s own gate, the place’s version in the film as decisions read it (ProjectController.view, §7.5) and theirs to write (PageAccess.editable; else error: {code: not-editable} or entity-unavailable), then submitting: <now> written, then the gate asked again — changed meanwhile, the item gets the gate’s error and loses submitting in one write (queue.submit.gateChanged), or, where that write fails, keeps its marker, unresolved — then the frozen request submitted through InsertionService.submit. A pass of N items is 2N + 2 record writes, each with its publication, and no read: the record’s, each item’s marker and its outcome, the end; an item not reached when a pass is cut short has no marker and waits for the next pass. A bound answer writes job; a refusal the insertion answers before any submission writes error: {code} and clears submitting; any other failure, or a record write lost after the answer (queue.submit.unrecorded), leaves the item submitting without a job — unresolved, never taken again by a pass. The record ends submitted where every item has a job or an error, else pending, never done; submit answers why a pass did not run, or stopped before an item (busy, no-record, not-pending, nothing-to-submit — queue.submit.refused — not-ready, or film-changed). RenderQueue.finishInterrupted writes a record left submitting with no pass in this app as a pass ends it, the unresolved items kept and nothing submitted (queue.finish, queue.finish.refused); markNotSubmitted(stem, n) clears one unresolved item’s submitting, refused while this app’s pass holds the queue (queue.unmark). A pass writes only into the film it started in: it takes the open film’s key before anything else and gives it to each of its record writes (PendingSets.update’s projectKey), which the film closed or another opened meanwhile refuses with nothing written; the pass then ends, the item in flight left as its own film has it — an answer not written is queue.submit.unrecorded with reason: film-changed — and submit answers film-changed. Logged, before anything is awaited, queue.submit.start {stem, film}, queue.finish.start {stem} and queue.unmark.start {stem, n}, every refusal with its ms; then queue.submit.items {stem, items, skipped}, queue.submit.gate {stem, n, outcome, submitting, ms} where the gate lets a marked item through, queue.submit.item {stem, n, target, model, platform, job|code, ms}, queue.submit.item.failed, queue.submit.done {stem, submitted, refused, unresolved, untouched, status, ms} and, where a record write fails before or after the items, queue.submit.failed. One app at a time renders a queue: two on one folder are not guarded against.

The queue listens to insertion snapshots from application startup, before any job can be restored. It keeps each first terminal view (done, failed or cancelled, or needsAttention where producerFailureOf(stateCode) says its producer’s refusal ended the render; no other attention state) in memory for the session, reading its phase, state code and reference from the emitted view itself, so dismissal cannot lose the outcome. This includes terminal jobs restored at startup. A terminal event with no open film keeps its outcome and logs queue.chain.skipped {film, moment, jobs, reason: no-film, ms} without requesting settlement. Subscription is logged as queue.chain.listen {film, reason, ms} and each first terminal view as queue.chain.heard {job, phase, state, detail, take, kind, reason, why, ms}.

A render’s failed job that a record of the open film holds has Details beside Dismiss, on its row in Activity and on its media tile (job:<id>:details), opening that record and logging jobs.details.open {job, stem, state, detail, from, reason}; a job no record holds has no such button.

The queue settles those heard outcomes when a new terminal view arrives, after a pass writes a job id, and when a film opens or the viewer changes. Only the open, ready film is considered. Each matching item in the viewer’s own queue records is written through PendingSets.update(stem, projectKey: film): take and its batch record at done, or error: {code, detail?} for a failed or cancelled job (stopped for cancellation), detail being the provider’s words where it gave them. The current item, job, outcome, target film and author are rechecked inside the update. A guard holds only while that item’s write is in flight; a failed write is logged and emitted as QueueChainFailure with the stem, item number, terminal phase, failure code and full message, and the next of these moments may try it again. There is no automatic retry. An outcome heard while another film or viewer is current is kept until the owning film and viewer return in this session. Closing the app first leaves an unrecorded outcome honest; there is no journal or reconciliation.

Each settle logs the moment, film, viewer and considered records/items as queue.chain.settle, and its completion as queue.chain.settled. Each write logs queue.chain.start, the current record and viewer rechecked under the write queue as queue.chain.check, then queue.chain.recorded or queue.chain.ended, or queue.chain.skipped with its reason or queue.chain.failed with the stage, error type and message. All carry elapsed milliseconds, and the item events name the job, phase, state, take and batch where known. A written outcome logs queue.chain.complete {stem, complete, total, rendered, failed, status, reason, ms}, the completion test’s answer and its counts. A terminal producer job that no record of the open film names logs queue.chain.orphan once per job; other job kinds are retained but do not log as orphans.

A queue is complete when it has at least one item and every item has a take or error; an empty record is incomplete. No new status is stored. Above Render, the header says {n} of {total} rendered, or, with any error, {n} of {total} rendered, {m} failed. The counts come from recorded outcomes; a stopped job counts among failed items. A completed header logs queue.page.complete {stem, rendered, failed, total, reason, ms} once per record while the page is open. The tree places complete queues with the day’s batches (§8.2), keeping each row’s id and selection. This rule depends on items, including a pending record completed by removing the last waiting item after a partial pass; no status is rewritten.

The pending batch’s page (filmopen_browser/sets/queue_page.dart, §9). A kind: queue record opens on SetListPage through BatchDetail; the browser reaches the pass through the state’s seam QueueRunner (queueRunnerProvider: submit, finishInterrupted, markNotSubmitted, isLive, job, act, changes, chainFailures), whose default answers not available and which the app composes over RenderQueue and InsertionService.current (lib/queue_composition.dart), so no product package imports the render UI. Each row reads its item: a take is Rendered, with its take and, where the film holds its record, Open the render batch; a job is its job’s phase — Rendered at done, The render failed; Activity says why at failed, else the render’s or the job’s own words — with the job’s own actions but Dismiss, which is Activity’s, a refused action said in Activity’s sentence; submitting is Sending… while this app’s pass of the record runs, else unresolved — Submitted? Check Activity with Mark as not submitted; an error is its sentence (queueRefusalText, one table for the pass’s and the codec’s kebab-case codes and the insertion’s camelCase ones, an unknown code shown as it is) with Add again on the viewer’s own record; anything else waits. The × is on the viewer’s current pending record only, and only on rows never submitted. Render is enabled on the viewer’s own pending record with an eligible item and no live pass, its tooltip the first reason that holds; it opens the record’s own page first, so that the page follows that record through the pass, then runs one pass; Render this batch from one app at a time sits under it. An own record left submitting with no live pass offers Finish the interrupted pass. Finish and Mark say done, nothing changed or failed, the reason in the runner’s log. The page draws outcomes and never writes the chain. The runner reads the live job first, otherwise the session’s heard terminal view without actions. A dismissed done job therefore still shows Rendered and its take while the record write is pending or has failed. A job neither the service nor this session knows, with no take, says Outcome not recorded with Open the place — no guess. The page listens to chainFailures while open: done says the take could not be recorded, while failed or cancelled says the end could not be recorded. With no page open the failure is logged only; no notification is replayed later. The next planned settlement moment may try the write again.

Add again adds the refused item’s target and request, unchanged, as a new item of the viewer’s pending batch — a new one while a pass holds theirs — and leaves the refused item as it is, never eligible; it is refused where the refusal was not-editable and the place, in this film, is still not the viewer’s to write (the pass’s own gate, the guards’ warn included); one is under way per item. No add joins a live pass: the page lists the viewer’s other pending queue records under Your other batches waiting:, each with Open.

A sent item whose error is its producer’s failure reads as its job did, with any possible charge first (§6.7): the provider’s words where they were kept, else the code’s sentence; any other error keeps the page’s sentences. Logged queue.page.error {stem, n, job, code, detail, reason} once per item read, queue.page.chainFailure {stem, n, phase, code, message, page, shown, reason, ms} for each received failed-write notification, queue.page.open {stem, status, items, states, render, finish, alsoWaiting}, queue.page.render {stem, eligible, skipped, film} and .done {code, ms}, queue.page.finish/.mark and .done {done, error, ms}, queue.page.act, queue.page.openRecord {from, stem}, queue.page.openPlace {stem, n, place, opened}, queue.job.act.start {job, action, reason: person} and queue.job.act {job, action, accepted, outcome: accepted | refused | failed, code, ms} (a warning where the service refused, and where it threw, then with error, the error’s type and text), queue.runner.unavailable {call} from the default runner, queue.chain {stem, n, job, phase} once per phase, queue.chain.start, .recorded {take, record, ms}, .ended {stem, n, job, phase, code, detail, ms}, .skipped {reason}, .unrecorded {reason} and .failed {stage, error, ms}, queue.readd.start {target, code, editable, guards}, queue.readd {into, newRecord, newN, ms}, .refused {reason, why, ms} and .failed {stage, error, ms}.

The dialog’s contract, filmopen_render_ui: RenderAction, Render on a place’s add tile (id render, for pictures, clips and sounds), opens a RenderDialog with a RenderContext — the place captured when the button is pressed — and answers the frozen request as the producer’s source, or nothing where the person closes the dialog; the dialog’s answer is a RenderAnswer — the request, and whether Add to batch was pressed rather than Render —, and where it was, the same request goes to the viewer’s pending render batch (PendingSets.add, Project Specification §12.7) as a RenderQueueItem — the place’s InsertionTarget JSON, the request’s JSON and when it was added, read back through RenderRequest.fromJson, the one validator, a shape it cannot read refused with a code (item, target, request, added, elsewhere) — and nothing is handed on, so rendering is the same one at a time or in bulk; Voice reads the request alone, its dialog offering no batch; a request for any other place is refused, so nothing the dialog does moves where a render lands. Whether it may run is its RenderAvailability’s. The dialog is RenderRequestDialog over a RenderBackend (§9.3): the models of the place’s kinds that it can give everything they require (renderModelsFor: the prompt, a negative prompt, a seed and a first frame, renderGivenInputs), the backend’s candidates and their readiness, a route’s options and quote, the picture a clip opens on, the prompt and the seed, and the request frozen when Render is pressed — the picture by its reference under the entry’s input name, first_frame, and the pictures the prompt names as reference_images (RenderRequest.inputs). At a block’s clip the inputs are the block’s own (clipInputsOf): its first and last frames and, where the clip covers that block alone, its voice line, sent as first_frame, last_frame and audio — each one the route’s row maps (renderRowMaps: of the platform’s rows marked render: true, else all of them, the way’s, else the first) and the way takes by format, size and a sound’s seconds (inputRefusal), the person’s, else the continued take’s, else the favourite, else the first; nothing is converted or trimmed. A clip’s own prompt (the block’s prompts.clip) names a frame’s media, and the pictures it names go as reference_images as a frame’s do — held to the model’s bound, refused no-placeholders on a row with no words for them, such as a text way’s; a reference way that takes no sound is given no voice line. The host freezes that frame’s bytes when the job’s call prepares, before JavaScript runs: a bounded, MIME-checked staged copy with SHA-256 in the call’s binding, verified before upload or embedding. The film’s filename is retained and the copy is released with the call’s unkept staging. A replacement during the queued wait can still reach preparation; the input reference alone is frozen at submission, and the copy and digest are never part of the receipt; a server started in advance on the GPU page (ServerOffersDialog, §9.7a), the dialog pointing there where a provider is on and no server is live, a deployment the provider refused staying in the window with its words — and saying so where a server was made all the same; where a model runs is read again whenever the servers change, the place chosen kept, else the first ready one, and a model that runs nowhere any more no longer offered. RenderRunsHere makes Render available where a manager runs over a backend and disabled, saying renders run in the desktop app, where none does; the application registers renderActionOver its manager’s backend at start. A voice is designed with the same dialog (Project Specification §9.1): a character’s voice_samples have Upload and Render as every place has, and Render there designs a voice while the character keeps none — no voice.provider_bindings id on any platform (renderDesignsVoice, logged render.voice.mode with the bindings read, the mode and the reason) — and speaks its audition text with the voice once one is kept. A design opens the dialog with RenderContext.designsVoice — the models of the catalogue kind voice alone (voiceDesignKind), the voice prompt starting at the character’s prompts.voice.positive, else its voice.description, and the words its previews say at voice.audition_text, held to the model’s bounds and sent as the param text; the request lands its previews in the place as any render’s takes do. A candidate that speaks a line in a voice the character keeps (RenderCandidate.speaksInVoice) is offered on a character’s places alone, takes the one voice.provider_bindings holds for its platform as the param voice, and Render is held, saying it keeps none there, where the character keeps none there; where its route names no price, a line is priced at the catalogue’s estimate for the characters typed, which the request freezes. A busy options answer with its chosen way not ready supplies no catalogue estimate; when that way remains ready and the route has no quote, the paid speech line retains the catalogue estimate for its unchanged words. A route quote earned for the unchanged parameters is used before that fallback; a busy answer for changed parameters holds Render without a price. A kept voice missing on the chosen platform is refused as speech, never designed again. Only the application imports the package.

One preparation for the dialog and the commands (filmopen_render_ui/render_preparation.dart). What the dialog decides before the person changes anything is plain functions the dialog and the render commands both call, so the two cannot drift: the source document (the open form’s draft, else the file), the offers at the place — at a character’s, location’s or prop’s reference pictures or sheets and at a block’s first or last frames only i2i entries where the request sends pictures and only t2i where it sends none, at a character’s motion clips only i2v, at an outfit’s sheets i2i where the outfit’s words name a picture and every kind where they name none, and at an outfit’s other places every kind (renderKindAt(target, inputs:), logged render.kind {inputs, kind, reason: tokens|carried|none}) — the prompt’s pictures (§9.8): the media its @ tokens name among the place’s candidates (renderPromptCandidates, the Prompt card’s writer kind), written once, in the positive words alone, in the access row the render is priced by (pricedAccess, its placeholders: {n} the picture’s position by first appearance) and sent in that order as reference_images, the document keeping its tokens (renderPromptInputs, substitutePromptTokens), refused before any request as tokens-missing where a token names nothing, references-count past the model’s reference_images max (a model taking none taking none) and no-placeholders where there are pictures and the row names no form — and the one a new render opens on (at a place with a kind, Settings → Models’ default for its kind, else the model picker’s default for the place’s kind at the entity’s stage, the last options standing between the two at any place, a block’s clip and a voice design looking up the default for their role, renderPicked and renderPickedOpening, as the dialog opens on it, §9.3, where the dialog alone opens instead on a ready server the person started when that default is not ready; elsewhere, or where the picker names none or its route is not offered, the dialog’s first model with a ready route on its first ready route), the treatment that applies and the prompt composed with it — the words given, else the entity’s own, else at a character’s reference pictures or sheets the selected writer’s default (promptTargetOf, renderWrittenPrompt, §9.3), written before the model is chosen — the writer asked with no model and handed the place’s media tokens (renderWriterTokens, each named as its chip is) —, a writer’s failure writer-failed — the way and the values the route suggests, asked again, twice at most, until its price is that of exactly them, the quote (the route’s where it priced exactly the choices or the route is free, else unknown), the stage — the treatment’s preset, the style chosen, carried as it is stored and never added to the words (§8.7) —, and the RenderRequest itself (a failure there at its request stage). prepareRender runs them over the provider container for a command, the kind decided by the prompt’s words as they stand — the words given, else the entity’s own, else the writer’s, written first, so that a picture they name makes it an edit — asking the picker for that kind — at a block’s clip i2v where the block has a usable first frame, else t2v, with the block’s givable inputs and the clip route mapping filter; at an outfit’s sheet with a resolved wearer i2i, carrying the wearer’s pictures; else t2i where the place names no kind — and opening only its row’s route, or the last options’ (§9.3); the prompt’s refusals are the commands’ codes; the words as sent — after their pictures’ words and the treatment’s — longer than the chosen way’s effective inputs.prompt.max_characters (renderPromptBound, a way’s in place of the entry’s; none stated, none invented; counted in characters) are not ready, prompt-too-long, as the dialog holds them, never cut; the dialog’s last options (§9.3), offered here, stand before the picker’s row and without one, their way and values settled the same. The route’s kind is lent or server by its destination, else paid where it bills — a saved key and FilmOpen credit alike — else local. With write: false, preparation refuses no-prompt before the picker or route options where the place has no saved words: the line at Speech; at Voice samples the character’s prompts.voice words while it keeps no voice, refused no-voice-description, else its voice.audition_text; else its Prompt card’s words. It never calls a prompt writer. Explicit words take precedence; otherwise a place with a Prompt card keeps its saved words even on a first-frame model. clipInputsChosen supplies the same defaults to preparation and the dialog: eligible media, favourite else first, exclusions and given reason under the chosen way, platform mapping and span. The dialog overlays its person’s or recorded preference. clipRefusalOf supplies the shared no-frame, no-last-frame, no-voice-line and needs-motion checks. Chosen and excluded input logs include each reference; a bulk refusal logs render.clip.refused with its target, reason, inputs, words and elapsed time. At Speech and Voice samples preparation asks the picker for tts and keeps its model alone. The unique candidate that speaks in the character’s kept voice supplies the route and params.voice; none refuses noVoiceRoute, several voiceRoutes. A block’s own line supplies its words and speaker; noSpeaker, speakerUnseen, noVoice or noDialogue refuses before offers. Voice samples use voice.audition_text once a voice is kept. While none is, they design one, as the dialog does: no picker is asked; the catalogue kind voice is offered, by kind and not by tag, opened on the first ready route (renderOpening, logged render.voice.design with the model, the route, why, the description and the audition); the prompt is prompts.voice.positive with its negative, no treatment applies, and the audition text, held to the model’s text bounds (audition-length), is sent as params.text where it is not empty; the request carries no voice, and its previews land as samples for the person to keep. The route decision logs keptVoice, platform and voice ID. Where no route quote is supplied, both paths call renderSpeechQuote for the catalogue’s character estimate, preserving the dialog’s busy-options restriction. Speech’s host arguments include the requested film’s language where known only while that film is open; another film supplies none. Only a route whose access row declares reference receives a granted live sample bound to its platform and exact voice id, with that entry’s own known audition words. Another active sample is never substituted; a missing bound sample is refused before the plug-in runs, and another open film refuses before the sample is described or granted. The state-owned QueuePreparation carries readiness, refusal code, route, model, platform, nullable Money cost and frozen queue-item JSON through QueueRunner.prepare. The app calls prepareRender(write: false) using the current render manager’s backend at each call; no installed backend answers unavailable, null preparation no-route. A ready result carries the request’s model and platform; a refusal after opening carries that candidate’s. Unknown cost stays null. queue.prepare.start records the target and no-writing policy; queue.prepare records the result, full item and elapsed time, or queue.prepare.failed records an unexpected error before rethrowing it. RenderQueue.submit(only:) and the state/app seam take only eligible items with the given numbers. Every other item stays untouched, so a record with waiting items ends pending. With no selection, the ordinary pass is unchanged. The initial nothing-to-submit decision considers the selection alone and writes nothing. queue.submit.start records only; queue.submit.items records notAsked alongside its taken and skipped items. The Workflow tab’s Render (§9.3) is the pass over chosen items: it prepares each checked place, shows what is ready, adds only those to the viewer’s pending batch and submits exactly their numbers, its Open naming that record. At outfit sheets, OutfitComposition shares the exact-epoch parent capture, live pictures, source snapshots, reference bounds and freshness check with the dialog. Bulk preparation uses the outfit’s own saved words and picture tokens after the wearer’s frozen references, asks the picker for i2i and logs render.kind {kind: i2i, reason: outfit, ref?}, preserving provenance and refusing an unresolved wearer. render.outfit.composed records outfit, wearer, inputs, words and elapsed time; render.outfit.refused records the reason and values at the refused stage. The early refusal logs render.prepare.refused with its target, words, reason and elapsed time. Clip kind selection logs render.kind with first-frame-given or none. Each other preparation logs render.prepare.start, a render.prepare {stage, ok, reason?, kind?} per stage (source, offer with the place’s kind, prompt, params, quote), render.prepare.prompt {source: person|writer|none, outcome?: ok|none|failed, code?, ms?, writer?: before-model, tokens?, words?} (the tokens handed to the writer and the words, the person’s or the writer’s) where the words came from — a writer’s none with why: noWriter, notAvailable, no-prompt or blank — and a throw past the host render.prompt.unexpected {type, error, ms}, render.picker {kind, stage, model?, plugin?, platform?, variant?, outcome: row|none|failed, reason?, code?, ms}, render.route {requested?, decision: accepted|noDefault|noRoute, opened?, reason?, ms} and render.picker.params {kept, dropped, variant?, asked, settled, variantAsked?, stack?} (the settings as asked and as settled), render.prompt.tokens {kind, tokens, resolved, missing, character?, characterReferences?, outfit?, words?, named, unnamed, ms} (named each token and the picture it names, unnamed the tokens naming none) (at an outfit’s sheet the character’s frozen pictures, the outfit’s named ones and words: own), render.prompt.placeholders {present, inputs, placeholders, references, words} (the form, the pictures in order and the words it wrote), render.prompt.length {ref?, model, variant, chars, bound, outcome: ok|refused|unbounded, prompt, ms} (the bound’s check, with the words it measured, variant: none where the model has no way, bound: none where none is stated) and render.prompt.tokens.refused {reason: tokens-missing|references-count|no-placeholders|references-needed, missing, given, bound, unnamed?} (references-needed the dialog’s, where the words name no picture and the model chosen needs one), then render.prepare.done {route, ready, basis, known, cost?, ms} (cost the price’s amount and currency), render.prepare.closed {ms} for a dialog closed without a request, or render.prepare.failed {stage, type, error, ms}; the request, once assembled (renderRequestOf), logs render.prompt.final {ref?, model, plugin, platform, variant, prompt, inputs, params}, the words as they will be sent after the tokens and the treatment; a command’s lines carry its call’s ref, the dialog’s none, and ms is the time since the start, which for the dialog includes the person’s choosing. A forced stage (Render at final resolution, RenderContext.stage, read by the dialog alone) is logged render.prepare.stage {source: forced}; the dialog refuses before composing or asking the picker at an outfit’s composed sheets (scope-outfit), at a place with no kind (scope-no-kind) and on a record naming any input but inputs_by_name‘s first_frame and reference_images, or another shape (input-unknown); it opens only on the accepted Final default (orFirst: false) — Settings → Models’ where one is chosen and offered (§9.4), else the picker’s —, else no-default for its life; and the take’s recorded inputs alone admit models and are sent, never the current selection, each model chosen refused where it does not take them (input-not-taken, references-count, frame-gone, frame-archived, frame-incompatible). Both doors of a take continued carry its recorded references through the same checks: Render from this take is given them too, its words the record’s as they were sent, its first frame chosen as before; the kind a continued render opens on is decided once the record is read. A forced stage logs render.final.start {take, kind, recorded: {first_frame, reference_images}, unknown}; both doors log render.final.carry {take, door: final|continue, model, first_frame: kept|none|refused|held, frame?, reference_images: n|none|refused|held, references} (the recorded first frame and pictures named whatever became of them; held where the other input was refused; {take, model, skipped: no-default} while no-default stands) and render.final.refused {take, code, model?, reason?}; a forced render alone logs render.final.frame {take, choice: recorded|changed} where the person taps a first frame and render.final.end {take, outcome: submitted|closed, refused, frame: recorded|changed|none}, take the draft’s render id as the record holds it, else the record’s stem.

Renders as commands (§7.3). Three builtin commands, each exposed to Remote control:

  • builtin/render-quote {target, prompt?}, read-only: {route, quote: {basis, known, currency, cents, per?}}, the quote the render would run under, prepared as above; nothing is inserted and nothing is spent.
  • builtin/render-image {target, prompt?, session, operation}, effectful. target names an entity at an epoch, a full stem as much as a versionless exchange id (versionsNamed), and the picture goes to the version a change would land in, workingVersion‘s (§7.3): the person’s draft, else their version in use. A render never forks: where that version is not the person’s own (another author’s official version, a library version; with no viewer at all, reason: no-viewer), both commands answer not-yours before anything is prepared, quoted, admitted or charged, with next: update-entity beside the working version’s reason: the model changes the entity first, which makes its draft, then renders. Where nothing is in use there, the answer is not-found. The target is decided on the view (§7.5), so a draft an update landed and that is not yet published is the version rendered on. After the film’s own check (no-project), the choice is logged before anything else as render.target {target, resolved, working: round|inUse|refused, author: person|other, reason, type, stem}, the stem as it is. The take is written as the person: the insertion’s authority stays theirs, and ContainerInsertionEnvironment.patchEntity writes as the viewer, never another handle (insertion.author {as: person|none}, none with no viewer). The render dialog’s own render is unchanged. The place is its media card’s first group, reference_images, where the page’s first Render tile renders. A project, a character, a location, a folder, an outfit and a prop have that place; any other type answers no-image-place, a target not found not-found, and no film open no-project (render-quote alike). prompt is the person’s words, else the entity’s words, else at a character’s reference pictures the selected writer’s default prompt — the commands’ own automatic writing, promptTargetOf‘s two places alone; the dialog writes no prompt of itself (§9.3). In order: the preparation (the media model picker naming no default: no-default with its reason — noModelPicker, the row’s own code or failed; its route not offered here: no-route; no ready route or way: no-route with its fixed reason, the route’s own — noKey, noGrant, … — noStack or busy; no words to send — an empty prompt with no treatment’s words, the dialog’s own rule, which the dialog reads from the same code — no-prompt; the words as sent above the model’s bound: no-route with the reason prompt-too-long; the writer asked for the default prompt failing: writer-failed, its code in the preparation’s trail); the registry’s admission (admit), whose refusal is answered unrun; then the insertion directly after it, ProducerSource(renderProducerKind, request) at the target, as Render inserts one — while the plug-ins’ settings are read again (PluginSettingsControl.current.machineSettingsSnapshot null) the call is refused before the admission, not-ready with the reason settingsLoading and no job, its operation key unspent, so the same key runs once they are read. The answer is {job, session, quote} as soon as onAccepted gives the insertion’s id; the operation ledger keeps it, so a repeated call under the same key answers the same job. Nothing refuses on price: a billed, estimated, per-unit or unknown price runs on the call, and the result carries the quote it ran under; a route that named no price is unknown, cents null, never zero. The observation names the entity, and the application says {who} started a render of {name}. An insertion refused before it accepts answers insert-refused with its fixed reason (projectNotOpen, entityUnavailable, unavailable, …); a failure after it accepted is the job’s to show at its place. With the settings whole at the call, the insertion binds at once under the viewer the admission checked. The race between the admission and the insertion is left unguarded and its outcomes are not promised: a viewer changed in that gap binds the new viewer; a settings reload there is refused settingsLoading by the insertion, answered insert-refused with that reason and no job.
  • builtin/job-status {job}, read-only, over InsertionService.jobView: queued, working, waiting and needsAttention answer {job, phase, progress?}; failed and cancelled {job, phase, code}; done names the take, take: {stem, kind, bytes}, resolved through the open film’s store by the view’s reference and read through its size from MediaStore.length, never its bytes (a take the store no longer holds answers failed) — a take that cannot be resolved is left out, the answer {job, phase}. For the relay alone, which reads the take whole to upload it, the call rechecks the registry’s gate (check) before any upload — a refusal answers it and nothing is uploaded — then mints the picture’s link once per job through MediaClient.upload, kept with the expiresAt it returned: concurrent readers share the one pending upload, a call after the expiry mints again, and a refused upload answers media-refused and is not kept. The answer carries a resource_link with the link and its expiry, and the picture inline only where the whole result stays under 1 MiB. The voice and the text assistant get the status and the take’s reference, no link. A job render-image or add-media started in this app run is watched until the insertion first shows it ended, and that view is kept — at most 256, the oldest dropped first — so its status still answers once a place that drew a done job has dismissed it. Status answers only for those jobs, live or kept: any other id is not-found, not looked up and not logged. An add-media job answers metadata, never its file, to every caller: {job, phase, destination: {stem, place, block?}}, with progress while queued or working, code and reason — the sentence its place shows — for waiting, needsAttention, failed and cancelled, and once attached take: {ref, name, kind, bytes}, the size from the store, left out where the film or the reference is not resolved, a take the store no longer holds answering failed as a render’s does; the same after its place dismissed it, never through the picture’s upload.
  • builtin/add-media {target, place?, block?, name, url | bytes}, effectful (session, operation): a file put into a media place of the entity’s working version, chosen as render-image chooses (not-found, not-yours with next: update-entity), as a drop puts it — FilesSource([MediaInput.bytes | MediaInput.url]) at SlotPlace(place), or with block a scene block’s BlockPlace(block, place), one entry, reviewed, preview set where the file has none. place defaults to reference_images and is any place of the type’s (entityMediaPlaces: an outfit’s references and sheet, a character’s sheets, voice samples and motion clips, …) or, with block, a block’s first_frames, last_frames, clips, voice_lines, music or sound_effects; any other is no-such-place (place, or no-blocks with a block on anything but a scene). The kind comes from name’s extension alone, refused wrong-kind where the place takes another (acceptsFile). Exactly one of url — fetched by the importer under its own rules, http or https — and bytes, base64, else bad-source (neither, both, base64, url where it is no address). No bound of its own: through Remote control the website’s 512 KiB request carries about 380 KiB of bytes; a larger file comes as a URL. not-ready and insert-refused as render-image’s; then the admission and the insertion, attributed to the chat that called (§7.4), as render-image’s insertion is too. A missing block or a refused fetch is found after the job is accepted: job-status says needsAttention with the code (wrongShape, badUrl, …). The answer is {job, session, stem, place, block?}; a repeated key answers the same job.

Each command logs render.command {ref, kind, args} … render.command.done {ref, outcome, args, ms} around render.target {ref, resolved, type}, render.command.refused {ref, code, reason?, args}, the preparation’s trail, command.admit, and render.insert.accepted {ref, job} or render.insert.refused {ref, code, error} — the importer’s problem code, else the error’s type — and render.insert.failed {ref, type, error} for a failure after acceptance (add-media logs the same lines as media.command, media.command.done, media.command.refused {ref, code, reason, next?, args} and media.insert.*, with its decisions media.command.target as render’s, media.command.place {place, block?, group, accepts, name, kind, decision, reason?}, media.command.source {kind, bytes?, url?, decision, reason?}, media.command.base64 {ref, error} where the bytes are no base64, and media.status {job, phase, code?, reason?, reference?, path?, bytes?, resolved?}), then render.job.terminal {job, phase, code, reference} when its end is kept (render.job.dropped {job} past the bound); the status logs render.status {ref, job, found, phase, code?, reference?, from: live|kept}, job the id asked, found or not, render.take {ref, resolved}, render.media {ref, decision, bytes} (link a first mint, cached a live or pending link shared, expired and refresh a link minted again, inline or link the picture’s own decision, refused with the media client’s code) and render.status.done {ref, resultBytes, ms}, or render.status.failed {ref, type, error, ms}. No line carries a link; args is every argument as given, the app run’s session — an identifier, not a credential — and the operation key included.

Auto-generate pictures (filmopen_render_ui/auto_references.dart, its box in the dock, §9.1). A preference of this person on this machine, assistant.autoReferences, default off (filmopen_state’s autoReferencesProvider, which is all the dock reads). The engine listens to the broadcast of landed writes (§7.4). It acts on a write that is attributed to a chat, of a character, location, prop or outfit, whose data changed: a create always; a patch by anything but media, preview, prompts and updated; a fork against the version it was forked from, with its header and bookkeeping keys left out as well, so that an identical fork is no change, and a fork whose source is not in the film counts as one. Neither the engine’s own writes nor a person’s edit of a prompt are heard. Every write told is logged auto.references.event {film, stem, kind, session?, attribution?, attributed}, and the decision auto.references.heard {film, stem, place, kind, session?, attribution?, attributed, changed?, why}, why one of off, unattributed, type, unchanged, again or render, and unchanged-since-prompt with the receipt’s after for a follow-up dropped as below; every engine event carries place, the media group the work renders into.

A person’s Pick is the second thing heard. An unattributed patch of one of those types in which an entry of reference_images present before without kept_at is present after with it, matched by its reference, and usable, is a Pick; an entry added whole with kept_at, a copy from Compare, is not, and a reference re-rendered without a Pick renders no sheet. It renders the entity’s sheet at Prototype: a character’s character_sheets, a location’s location_sheets, a prop’s prop_sheets, an outfit’s outfit_sheets where its wearer has a usable picked reference; and a character’s Pick also the outfit_sheets of every outfit in use at its epoch that it wears, that the viewer wrote and that has a usable picked reference. An entity whose wearer is the character OutfitRenderComposer.resolveParent reads is the only pairing; a pair that is not whole renders nothing, auto.references.skipped {why: notPaired, outfit, wearer, reason}. Each sheet’s token names its own holder’s entry: the entry just picked, or on a character-triggered outfit sheet the outfit’s latest usable picked entry by kept_at, the wearer’s pictures riding in through the outfit composite. Logged auto.references.heard {film, stem, place, why: pick, entry, keptAt, changed, trigger, busy}.

One job per entity and place at a time, the work known by its film, stem and place and marked busy before the first await: a write while its job runs keeps that write’s receipt and asks for one more render when the job ends, which reads the document as it then stands (auto.references.again), however many writes came — unless that follow-up’s document, media, preview, prompts and updated aside, is the one the job’s prompt was asked from: then it renders nothing and asks no writer again on the same data, whatever that prompt came to (auto.references.heard {why: unchanged-since-prompt, after}; the job’s own auto.references.prompt or skipped line says how it ended). A sheet’s follow-up is compared on the pictures it would be made from, the wearer’s picked entry and the outfit’s on an outfit’s sheet, so that a new Pick on either side renders once more on the latest pair and the same pair ends it (auto.references.skipped {why: unchanged-since-pick}). A created entity is heard so: builtin/create-entity publishes its seed and its content in one pass (§7.3), so, heard on the seed’s receipt, the prompt reads the film with the entity whole, and the content’s receipt says what that prompt was written from. There is no queue, timer, cap or retry of the engine’s own. The writer waits its turn: the stock writers’ plug-in runs one call at a time, so during a press each entity’s prompt is asked with wait, the plug-in’s own queue of §6.11 (plugins.call.slot says each call held, admitted and its time), with no bound on that wait, and with an admission check asked once the call has its turn and slot, whether or not it waited: the box off or the film left, the writer is not started and nothing is spent (auto.references.prompt {decision: dropped, why: off | filmLeft}). A job ends as the insertion ends: the engine watches the insertion service itself, under the render manager’s attention the app shows over it (handed to it by the root’s wiring, §7.3), so an acknowledgement still pending is no end; done — the take attached — is rendered, and failed, cancelled or needsAttention (an acknowledgement that failed after the attachment, renderRecordUnsettled, or an attachment refused) is not rendered with its state’s sentence, as is a job the views listed and no longer list, dismissed while it waited (terminal: cancelled, code: dismissed), freeing its entity and its line. The box is read again as a job starts, after a read of the line the job waited for, and just before it submits, so unticking it stops what is not yet accepted and no writer is asked once it is off; a film no longer open is refused at the same points and after the writer; an accepted render is not cancelled.

The place’s prompt (prompts.reference, or on a Pick prompts.sheet, its writer handed only the picked entry’s @reference-n) is written again by the place’s writer where it is empty or a writer’s (plugin and writer present), whatever its age; a person’s own words stand, tested again inside the patch — except that on a sheet the words, a person’s or the writer’s, must name the picked entry’s @reference-n and no other: anything else would send other reference pictures than the one picked, so it is a conflict, said under the box, logged auto.references.skipped {why: pickConflict | writerConflict, named, picked, words}, nothing rendered or billed, a person’s words left as they are and a writer’s not written. It is written through the controller’s patch door under the triggering write’s attribution where it has one, so an assistant’s is published and stays in the chat’s item and a Pick’s is the person’s. auto.references.prompt {decision, target, tokens, …}: person with the words that stand, or with the writer’s answer left unused where a person’s words came while it wrote; written with writer, llm, words and negative; conflict with why, words, named and picked; none or failed with writer, code and reason where the writer gave no words, dropped with why, writer and the call’s code where its admission check said no and it was not started, failed with the words and code where the patch was refused. A writer without words leaves the reference unmade, and no render goes out on older words.

The render is prepareRender(…, write: false, stage: MediaStage.prototype, remembered: false, prompt:, negative:) and the insertion service at the work’s place, sent on the words the engine holds — the writer’s it just wrote, or a person’s that stand — and not on the film as last published, where a prompt written while another gesture holds publication (a create’s, §7.5) is landed but not yet in it: always Prototype, on Settings’ or the picker’s default for t2i or i2i, never a remembered route; a sheet is agreed only against the line’s i2i route, an unset one being auto.references.skipped {why: noRoute, needs: i2i, line}, and where that route’s quote has no amount the line says the price is not known before the render. The job is submitted under the triggering attribution where it has one, and its attachment runs in the zone it was submitted in (§6.13), so the landed picture keeps the chat’s item and a person’s own render or upload stays the person’s. auto.references.render {stage, model, platform, route, quote, words, negative, job?, terminal?, code?} says each step: a preparation that is not ready and a refused submission by code, the submission by job, the job’s end by job, terminal — the insertion’s phase — and code, its state code; a submission that throws is also logged whole, auto.references.failed {stage: submit, code, type, error, stack, ms}.

The agreed price is the line under the box (auto.references.setting {why, on, agreed: current, line: {text, writer, t2i, i2i}, ms}, each route with its label, model, platform, route, way, place and quote). It names the routes at Prototype for t2i and i2i with each quote, in the render dialog’s wording, and the writer of the prompts — character-reference‘s, the one the stock plug-in’s other reference places share —, or No route — pictures are not generated., the route’s absence logged auto.references.noRoute {kind, picker, why, notReady}, kind: all and why: noBackend where this build has no render backend. The box is ticked with that line in view; the agreement is what the line says now, a kept tick carrying over as the line is read again, which it is when the dock draws the box, the box changes, Settings’ defaults change, Use the LLM changes (why: textModel, so that the line names the writer that will run), the writer’s route changes — a route chosen, a program set, credit answered: on the plug-ins’ changes, read again only where the writer’s plug-in or route identity (funding with its slot, program or key) is no longer the line’s, logged auto.references.route.changed {was, now} and read why: textRoute — and a render it did not cover is refused; no other change of the plug-ins reads it. Each read logs that it was asked (auto.references.line.asked {why, busy}, a request during a read asking for one more after it), that it began (auto.references.line.start {why}) and each step before it is awaited (auto.references.line.stage {stage: catalogue | picker | offers | options, kind?, ms}). Work the line does not name is not done: another model, platform, way of running it, place, funding — the same route moved from Your key to FilmOpen credit at the same price included, the preparation carrying its candidate’s funding — or quote is left unmade and the line read again (auto.references.skipped {why: notAgreed, line, model, platform, route, stack, funding, destination, quote}, route naming the plug-in, platform, key slot, program and way on each side; a route that runs on a program — Codex on a plan — is asked for its options and agreed with that program, as a preparation requests it), as is a paid writer of the entity’s own place that is not the one named — its model, its price or where it runs ({why: writerNotAgreed, writer, quote, agreed, agreedQuote, origin, agreedOrigin}); a writer whose route moved while it waited its turn is not started either (auto.references.prompt {decision: dropped, why: writerNotAgreed, origin, agreedOrigin}), the line read again —, and where the line has no route it is read again before that is decided. auto.references.skipped also says off, filmLeft, noRoute, gone and noBackend. How the last automatic render went stands under the box in a sentence (auto.references.item {stem, state, line}), the logged code beside it. An unexpected failure is auto.references.failed {stage, type, error, stack, ms} and a sentence pointing to the log, stage being line where the line could not be read. The job’s events name the film, the stem, the session, the triggering attribution and the time the job has run.

Its activity and Stop. The engine shows its work to the dock as it stands, one AutoActivity per entity whose work runs, from its job’s start to its end, follow-ups included (autoReferencesProvider’s activity, in the order the work began): its film, stem, name and the chat whose write started it, its phase — prompt while its prompt is waited for or written, render from the render’s preparation to its job’s end, with the job once submitted — and whether a Stop is settling it or could not be confirmed; the line goes when that work ends, rendered, refused or skipped. There is no total to count from and none is kept: a line says what its work is doing now. Stop (AutoReferencesController.stop, the dock header’s, §9.1) stops all of it, the box staying ticked: each prompt’s writer is asked with a PluginCallControl, which Stop fires, so a writer still waiting its turn in the plug-in’s queue is removed and a running one is signalled (§6.11); the work is marked stopped and its follow-up write forgotten, and the mark is checked at the writer’s admission, after its answer — a late answer is neither written nor rendered —, again inside the prompt’s patch, which may have waited behind another write of the entity (auto.references.prompt {decision: dropped, why: stopped, at: patch, writer, words, code, ms}, nothing written), and before the render is submitted; a submitted render is asked to stop through its job (InsertionService.act(job, JobAction.stop)), and a job accepted while Stop was asked is asked as soon as its id is known. Each line reads Stopping… until its job ends; a stop the job refused (a job still running, finishing say) stays on its line with its reason in words until the job ends, and one the render could not confirm (waiting:stopUnconfirmed, §6.13) stays so while the job waits for its own Retry or Dismiss on the Activity page; a second Stop leaves a line a Stop has reached as it is, and the follow-up heard after the first. The mark ends with its job, so a write heard after Stop — even while the stopped work settles — is new work and runs. Each setting of the lines is logged assistant.activity.changed {lines, reason, source: autoReferences, ms} as §9.1 gives it. Logged auto.references.stopped {queued, inFlight, cancelled, again, ms} (stems), auto.references.stop.job {film, stem, job} and auto.references.stop.job.ack {…, accepted, code, ms}, auto.references.prompt {decision: dropped, why: stopped, admitted, words}, auto.references.skipped {why: stopped}, and per line assistant.activity.stop.outcome {kind: prompt | render, session, film, stem, callId, job_id, outcome: cancelled | removed | signalled | refused | unconfirmed | finished, code, ms}, ms since the Stop.

The boundary: filmopen_render depends on filmopen_jobs and filmopen_format and imports nothing else of FilmOpen, no Flutter and no library the web build lacks (its boundary_test.dart); no package but the application depends on filmopen_render_ui, and neither the format, the jobs, the insertion, the media, the state nor a page reaches filmopen_render, directly or through any package between (test/render_boundary_test.dart).

A prompt writer’s LLM is separate from the media route being prepared: the host captures the shared Use the LLM: decision before the writer runs and passes its catalogue id and entry in buildPrompt.args.llm. That call and nested calls keep the same decision; ctx.ai.complete refuses noProvider with its reason where no LLM was reachable. render.prompt.start includes this handover. The media model picker continues to choose the render’s image, video and voice defaults.

filmopen_comps is a pure Dart package whose caller supplies a comp’s general JSON, including the picked name, visibility and details, and the ordered media JSON with each render’s supplied metadata and details. Local byte capabilities are supplied separately by opaque source id; paths and account bearers are not public fields. Missing costs, providers, stacks and other facts stay null. An omitted name is the UTC date/time with the supplied generation type. Visibility defaults to unlisted; unlisted is shared by its link and left out of the public directory. The draft has no author: the publishing service writes the account’s username, so nobody signs another’s name.

The assembler copies originals into a newly owned workspace, measures SHA-256 and byte counts, and generates separate JPEG previews through an injected media strategy. A native file workspace is a new temporary directory, its access what the platform gives one; its failures carry where and why as their detail. Portable preparation decodes bounded still images in an owned native isolate, a PNG’s, JPEG’s or WebP’s width and height read from its header before any pixel is decoded, so an image over the pixel ceiling is refused before its pixels are; an original is the person’s own file, so a crafted one’s compressed data or animation frames are not bounded beyond that; a refusal’s detail says why, with the values it decided on, and reaches the log with its code; browser decoding is synchronous, with queued cancellation delivered before returning the result. A picture always has a preview; a clip or a sound may have none, and the portable strategy makes none for a clip, whose facts are the caller’s or unknown, so every kind publishes without a native process. The bundle is the uploaded comp.json first, then the media and their previews; it contains no HTML or script. Its digest is SHA-256 of the compact inventory of path, bytes, digest and content type. A prepared bundle names its project (the project’s tag), its slug (15 characters from a secure generator, the unguessable segment of its address) and its revision; a given revision and slug prepare the same revision again from the same sources. It permits at most 128 cards, 64 MiB per original, 512 MiB per bundle, 128 KiB per manifest and 256 KiB per preview. The caller can only tighten these ceilings. General and per-media details are bounded JSON objects, copied deeply, with prototype-member keys refused; a key named like a credential is copied as any other, the service deciding what it accepts. Numbers are finite and within the browser’s exact integer range; larger integers use strings. Cancellation releases a stalled source-length future without waiting for that caller operation to finish. Native mutations cannot overwrite an existing artifact concurrently. Failed disposal is retryable and never replaces a preparation diagnostic with a filesystem path.

CompsPublisher speaks the publishing contract /v1. It receives the existing account’s fresh-token callback, a caller-owned HTTP client and the publishing host, which the application chooses from its domain alone; it sends over HTTPS only, follows no redirects, and bounds requests and responses. One deadline covers the response headers and body, releasing owned response streams on timeout or cancellation. Upload retries retain the same revision and inventory; confirmed files are skipped and completion is idempotent. The completed answer is the published receipt with the comp’s public URL; a resume of a published revision answers that receipt, and its status can be read by revision alone at any later time. Deleting a published revision is refused by the service with its receipt, which the publisher answers as published. A refusal the service names keeps that name as its code, with the HTTP status beside it; any other keeps a generic code by status. Every failure answered by the service carries the status and the first 32 KiB of the body for the caller’s log.

The assembler and the publisher report every stage to an optional event sink — each preparation stage with its values and a refusal with its cause, the sizes and the limits it was decided on; each request before its bearer is attached, each answer with its status and body, a wait that ended without one with where it stopped (the token, the headers or the body), the status already received and why, each retry and refresh with its reason, and each outcome with its elapsed time — so the caller logs them as they are and no event holds a credential. No widget, project writer, account initialization or upload occurs at application startup. INTEGRATING.md describes the app-owned media strategies, the account callback, the events and the prepared bundle’s lifecycle; the package keeps no journal across application restarts, the caller’s record of the revision being the resume.

The application composes it in lib/comps_composition.dart, installed off the web over filmopen_state’s compPosterProvider (whose default answers every call not available and logs comp.poster.unavailable with the call), as the timeline’s engine is installed. A comp record (Project Specification §12.7) maps to the draft: name, description, and visibility public → listed, private → unlisted; each item, by its own n — never renumbered — is media m<n> with source s<n> (a take’s stem is no comps id; an item without its number is refused bad-item), its version stem in details.source, its kind from the take’s deliverable (a picture deliverable an image, cl a video, a sound an audio) — else from the medium’s own file, one of no comps kind refused unsupported-format —, its label the entity’s name in use and the deliverable; its record the one the item names, else the batch its take’s name carries, whichever reference form names the take; from that record’s item where it measured them width, height and duration in seconds — a clip’s or a sound’s missing ones then from the selected timeline engine’s probe, a refused probe leaving them unknown —; from its render record model, provider (the platform), plugin, prompt, price (cost_usd in US dollars), renderSeconds, details.params and details.stage — each absent where the record lacks it, never zero. Each medium resolves through ProjectMedia and the media store to a local file; a URL, a session copy’s bytes, a reference no file answers to, or a project without a media store is refused no-file, and an unexpected failure failed. Posting is two calls, so the caller can write the record’s status posting before any request: prepare composes and prepares the bundle in a fresh owned workspace with the portable preparer, sends nothing, and answers the posting — the comp’s stem, revision, slug, the project’s tag, the account signed in, the draft and each source’s file — or refuses no-project with no project open and signed-out with nobody signed in; publish sends it, the bundle held since prepare — released when the publish ends, whatever its outcome, so no bundle is held across posts and a post is never resumed — or, where none is held, prepared again for the same revision and slug from the posting, and refuses account-mismatch without a request unless the posting’s account is signed in at its start and is each token’s own: every token the publish asks for is sent only where its subject (the JWT’s sub, read, not verified) is the posting’s account. check and abandon read and delete a revision by its id, asked only as the posting’s account, a revision published first answering its receipt; the comp’s page calls neither, since a post that did not finish is posted again as a new revision (§9). One post runs at a time, and cancel stops it. An outcome carries the receipt’s state and URL, or the refusal’s code and the service’s status and body, and says whether the revision’s fate is known: a receipt published or cancelled, or a refusal the contract calls definite; anything else — a timeout, a server error, an authentication or quota answer, a local cancel — leaves it unknown. The publishing host is publish.comps.<kFilmOpenDomain>; the token is the account’s freshAccessToken, asked for only when a request goes. Each operation logs comp.post.start with its operands before its first wait, comp.post.step for each step and decision — at compose each item with the record chosen and why, each mapped entry with where its kind came from, each probe and what it filled — and comp.post.token as each token is asked for while publishing and as it is checked, with the posting’s account and the token’s subject, never the token; each stage ends with comp.post with its stage, outcome, code, detail and elapsed time and the values it worked on: the stem and the draft whole at compose; the stem, the project, the account, the revision, the slug and the bytes at prepare; at publish the stem, revision, slug, the posting’s account and the one signed in, whether the bundle was held, the state, the URL and whether the fate is known; at check and abandon the revision, the account signed in and the outcome. comp.post.release names the stem and revision as a publish ends, whether a bundle was released and the outcome’s state or code; comp.post.cancel says whether a post was running, and every event of the package is logged as it is under comps.<name>.