Compatibility and deprecation¶
What you can rely on between Scroll Studio releases, and how things change when they have to.
Versions¶
Scroll Studio follows semantic versioning: MAJOR.MINOR.PATCH.
- Patch (1.4.1): fixes only. A site that built before builds the same page, except where the old page was wrong.
- Minor (1.5.0): new features. Everything that worked keeps working; deprecated things warn.
- Major (2.0.0): may remove what was deprecated. Each removal is listed in the migration guide.
What the promise covers¶
| Surface | What stays the same within a major version |
|---|---|
The spec (site.yaml, engine/spec.py) |
Every valid spec stays valid and builds the same page: same sections, words, order and layout. New fields are optional, with defaults that keep today's result. |
The built page (the runtime in engine/compile/static/runtime/) |
A built site keeps working as a static folder with no server. Its scroll behaviour, timing and accessibility do not change without a spec field asking for it. |
The CLI (studio ...) |
Commands, options and exit codes keep working. Output meant for people (tables, progress) may change; anything documented as machine-readable (--json) does not. |
| The MCP tools | Tool names and their inputs keep working; new inputs are optional. |
| The style contract (engine/styles/README.md) | A project style written to the contract keeps rendering. |
| Theme presets and styles | Keep their names and character. Colours may be adjusted to meet the contrast rules. |
Not covered: internal Python modules and functions (anything not reached through the spec, CLI or MCP), the cache
layout under .build/, the Studio app's internal API, and model choices (a better model may replace a worse one; the
spec field that selects it keeps working).
How something changes¶
- Deprecate. The old field, option or tool keeps working and warns, whenever a spec is validated or the command runs, naming the replacement and the version it will be removed in. The docs and the generated reference mark it deprecated.
- Wait. At least one minor release with the warning; longer for anything common.
- Remove, only in a major release, with an entry in the migration guide showing the exact rewrite of the spec.
A pull request that changes any surface above is labelled breaking-change and carries a migration note in its
changelog fragment, whether it deprecates something or (in a major release) removes it.
How the promise is checked¶
- Layout snapshots of every example (
tests/test_layout_snapshots.py): what each spec compiles to. A change shows up as a diff that is reviewed like code. - An old-spec corpus: the examples from every release tag must still validate and compile.
- Schema, CLI and MCP snapshots: a change to the public surface shows up as a reviewed diff.
- Conformance tests for every theme preset and style.
How to deprecate a field¶
Mark it where it is declared in engine/spec.py. Nothing else changes: it still validates and builds.
from engine.spec import deprecated, deprecated_all
# a whole field
tagline: Optional[str] = Field(None, description="...", json_schema_extra=deprecated("2.0.0", "`description`"))
# one value of a field (several: wrap the marks in deprecated_all)
layout: Literal["flow", "stack", "pile"] = Field("flow", json_schema_extra=deprecated("2.0.0", "`stack`", value="pile"))
deprecated(removed_in, replacement=None, note="") takes the major version that removes it, what to write instead
(in backticks) and, if needed, a note. engine.spec.deprecations(spec) returns a Deprecation for every use in a
spec (its path, the section it is in, removed_in, replacement and a text sentence). studio build prints each
as a warning, the set_spec MCP tool and the Studio app's spec save return them as deprecations, and the generated
reference marks the field. The mark is in the JSON Schema (deprecated: true, x-deprecated), so the schema snapshot
changes: update it in the same pull request, with the migration note in the changelog fragment.