§1Two lifespans

An engine that ships, work that endures

Engineversion 1.8f16 trainingsecure aggregation v1content declaresSaved workflowf16 trainingloads & runsA newer packsecure aggregation v2needs a newer Scellis
Fig. 1 — The compatibility handshake: the engine names what it can do, content declares what it needs. A match runs; an unmet need is refused loudly — never mis-executed.

The application you download is deliberately small — an engine, and nothing else. Every capability you actually use — blocks, kernels, models, viewers — arrives as content from the catalog, identified by the hash of its bytes. Your work never lives inside our binary, so our release cycle cannot hold it hostage: the two move on separate clocks, joined by one explicit contract.

The contract has two halves. The engine carries a version number and a list of what it can do — f16 training, secure aggregation, and so on — and content tests for capabilities, never version numbers. Content carries the list of what it needs. When the running engine cannot satisfy a declaration, the loader refuses loudly: a clear diagnostic and a “needs a newer Scellis” badge in the catalog — never a crash, and never a silent mis-execution, because a wrong result ages far worse than a refused one. How the engine declares what it can run is in the guide.

§2Backward compatibility

A newer engine runs everything the old one ran

ChangeRelease classYour existing work
A new capabilityadditiveRuns untouched — your content never referenced the new value.
Any new engine releaseverified before releaseThe frozen golden corpus must re-execute successfully before a release exists.
Retiring a capability, changing its meaningmajorA migration path is required — never a silent drop.
A new version of a block or opcoexistsPins keep resolving the old version — published versions are immutable.
Content newer than your enginerefused loudlyA “needs a newer Scellis” badge — never a mis-execution; you update when you choose.

The requirement is mechanical: automated checks re-execute the frozen corpus against every engine change and keep every declaration honest — before anything ships.

Backward compatibility here is a tested requirement, not a changelog courtesy. A frozen golden corpus — sample ops, blocks, and models from every earlier engine — is re-executed by the current engine, within its declared tolerances, before a release can exist. If a change would alter an old op's numbers or break its execution, the release is blocked: correctness is versioned the same way it is proven.

The engine grows by adding, never by rewriting: a new capability is a value your existing content never referenced, so that content runs untouched. Retiring a capability or changing what an existing one means is a major release with a migration path, never a silent drop. And published content is immutable by identity: a version is a contract, new versions coexist beside old ones, and anything pinned keeps resolving the exact bytes it always did.

§3The update path

Updates are atomic, verified, and taken on your terms

New shell fetchedat next syncIntegrity-verifiedAtomic swapReload to updatewhen you chooseno network?your deviceengine 1.8 · cachedkeeps computing
Fig. 2 — The update path: fetched at sync, verified against a signed hash, swapped in one step, taken on your next reload. Offline, the cached engine keeps running.

An engine update comes down the same path as content. The new app shell is fetched in the background and its bytes are verified against a signed hash before it is activated, then swapped in one step — a half-updated app never runs, and your local database is untouched. What you see is a calm “reload to update” signal carrying release notes: the new version and exactly what it changed (changelog) — never an app that quietly changed under your feet overnight.

Decline the reload — or lose the network — and nothing degrades. An offline device keeps computing on its cached engine indefinitely: the local-first working set is complete on the device, so a laptop in the field can stay on one engine for a year and keep drawing, running, and training the whole time. An update here is something you take, not something that happens to you.

§4State across updates

An update never bricks a returning tab

On your deviceAcross an updateMechanism
Drafts & un-synced editspreserved, alwaysDurable storage only ever grows; every automatic repair keeps it.
Committed workbyte-identicalAddressed by hash — the bytes at a hash never change.
Your copy of the catalogre-derived in secondsCleared and rebuilt through normal sync when the stored shape changes.
The app itselfswapped in one stepVerified against a signed hash before it is activated.
Settings & profilespreservedDurable state — it only ever grows, never destructively.
A wedged tabone line to recoverA documented reset in the address bar reloads clean — no developer tools required.

Releases are proven, not hoped: a returning-user check loads the fresh app against an old database and a stale cache, and must repair itself to a working Studio.

Local-first makes “coming back with old state” the normal case, not the edge: a new release routinely meets a browser still holding the previous one's caches and local database. So the shape of everything stored on your device carries its own fingerprint. On startup, before any stored data is touched, a changed fingerprint clears exactly what can be re-derived — the catalog copy, the caches, the app shell — and rebuilds it through normal sync in seconds. Invalidated and re-derived, never migrated in place, never left half-consistent.

What only your device holds — drafts, edits still waiting to sync, your settings — is preserved by every automatic repair. Only you can clear it, with one documented reset in the address bar; that same one-liner is the recovery for any wedged tab, with no developer tools required. Even a misbehaving background worker is recoverable by design, so a bad update can never become a permanent wedge.

§5Reproducible for years

A run remembers the engine that produced it

Reproducibility across releases is recorded, not remembered. Every run pins everything it used by hash *and* the engine version that executed it, at a declared strictness — so reproducing a result years later starts from a machine-readable statement of what ran, on what, and how strictly. Cited, committed work resolves to identical bytes for as long as the link exists, and a reproduction replays on any engine that satisfies the recorded contract.

Where the guarantee has an edge, the record says so instead of rounding up: GPU floats re-execute within the tolerance the run declared, and bit-exactness across devices takes the CPU reference path — boundaries we print in honest limits. The result is reproduction that stays honest across years: the claim travels with the run, calibrated, rather than decaying into folklore. The working practice is in the reproducibility guide.

§6Beyond our roadmap

Longevity that does not depend on trusting us

A longevity promise is weakest exactly when you need it most — so the exits are built in. Everything you make exports as portable, documented .scellis-* files — workflows, Packs, models, datasets — reproducible byte for byte, verifying their own hash on import; plus a device-local backup of everything on your device (credentials excluded by construction: they never leave it). What you author is yours, and it leaves whenever you say so.

The formats survive without our goodwill: the schemas, the export files, the conformance corpus, and the CPU reference are published permissively — the interchange ring — so anyone can read, verify, and build on your work without asking us. The engine itself is published as source under fair-code terms, and each release converts, in time, to the permissive license. We plan decades of releases; the point of the architecture is that your work does not have to bet on that.