Docs·Delta DSL Script·Concepts

Versioning & Migration

How DeltaDSL handles script edits, BE-tracked versions, rollback, and the function-update story. What happens to existing scripts when the language adds new functions or changes the runtime.

DeltaDSL scripts live on the backend and are versioned. Every save creates a new version; the user (script owner) can roll back; users who installed your script keep working against the version they installed (or auto-upgrade on the next chart session, depending on your source_access and visibility settings).

This page covers two questions:

  1. What happens when the user edits a script — the version model, the rollback flow, and how changes propagate to other charts.
  2. What happens when DeltaDSL itself adds new functions — backwards compatibility, the runtime upgrade story, and what scripts need to do (almost nothing).

Per-script versions

Every script has a small set of canonical fields tracked by the backend:

FieldMeaning
id (usc_…)Stable script identifier.
slugURL-friendly handle.
name, description, tagsFree-form metadata. Editing these does NOT push a new version.
visibilityprivate (only owner) / unlisted (link share) / public (marketplace).
source_accessopen (source visible) / closed (compiled only — used for paid scripts).
current_version_noInteger that increments on every save.
current_version_idStable id for the active version.

The source code is stored per-version, not per-script. Every save:

  1. POSTs the new source to /v1/scripts/:id (or /v1/scripts for new scripts).
  2. The BE compiles the source. If compilation succeeds, a new version row is written with version_no = previous + 1.
  3. The script's current_version_no flips to the new value.
  4. The compile log, params schema, and alert manifest are recomputed and stored against the new version.

If compilation fails, the BE rejects the save with the compile log. The script's current version stays unchanged — your existing chart continues running the previous version while you fix the script.

What counts as a "save"

Only source-bearing edits push a new version:

  • The script source code changed (different bytes).
  • The script name changed (this is part of the canonical record alongside source).

Pure metadata edits (description, tags, visibility, source access) update the BE row directly without a new version.

The dialog auto-saves on apply, on unsaved-change blur, and on the explicit Save button. Each of these calls the same chokepoint — saveScript({ source, name }) — which is responsible for the dedup check.

Listing and rolling back

Owners can list every prior version of a script and roll back to any of them.

UI flow (Custom Script dialog → Versions panel):

  1. Open the script in the editor.
  2. Click "Versions" in the dialog header.
  3. The dialog fetches the version list from /v1/scripts/:id/versions and shows each one with timestamp, version number, and the first line of the source as a preview.
  4. Click a version to load its source into the editor (read-only preview).
  5. Click "Activate" to flip current_version_no to that version. Every chart that has the script enabled re-binds to the now-current source.

Activating a prior version does NOT delete newer versions — they remain in the history. Activating works as a pointer flip, not a destructive rewrite.

Rollback is the right tool when you've shipped a regression and need to immediately restore the previous behaviour. Once activated, every chart that has the script enabled refreshes its bridge binding on the next register cycle (typically within a second).

How charts pick up edits

When a script's source changes (either via your save or via your rollback), the change propagates to every connected chart:

  1. The store sees the new current_version_no and patches the in-memory cache.
  2. The registry's register() chokepoint detects the source diff and tells every active chart bridge to recompile + rebind.
  3. Each bridge calls bridge.removeIndicator(indicatorId) for the old binding, then bridge.addIndicator(name, sourceCode, params) to wire the new one.
  4. Every WASM pane re-runs the script against the visible OHLCV window and repaints.

This is fast — typical scripts compile in 20-100 ms server-side and rebind on the chart in under one frame.

Persistent slot continuity

Persistent shapes (labelNew("foo", …), boxNew("bar", …), drawingHline("level", …)) keep their identity across script saves AS LONG AS the slot name is unchanged. Edit the script, push a new version that still calls lineNew("trail", …), and the line carries over.

If you rename a slot (lineNew("oldName", …)lineNew("newName", …)), the old slot is treated as orphaned and may be cleaned up. Rename slots only when you intend to retire the old one.

What happens for users who installed your script

Public / unlisted scripts can be installed by other users. When the script's current_version_no advances, every installer sees the change at most one chart-mount later — the installer's chart re-fetches the current version on next bridge register.

Installers don't see version history (that's an owner-only view). They see the script behave the way the owner most recently shipped it.

If you're shipping breaking changes (renamed inputs, changed output panes), give your installers a heads-up — there's no in-app changelog mechanism today. The pragmatic options:

  • Keep behaviour stable and only add new optional inputs.
  • Bump the script name (or fork into a new id) for major redesigns.
  • Roll back if the new version creates pain reports.

Language versions — @version

The PREVIOUS section was about your script's version (your edit history). This section is about the DeltaDSL language's version — the rules that govern what every script in the system gets to see.

There are two versions in play. Don't conflate them:

VersionBumped byScope
Script version (current_version_no)You editing your own scriptYour script only
Language version (DeltaDSL v1, v2, …)Platform shipping a runtime updateEvery script in the system

Today the language is at v1. Every script is treated as v1 by default — whether it explicitly declares so or not. When (if) the language ever ships a v2 with breaking semantic changes, your existing v1 scripts will keep running on v1 semantics. You opt into v2 by editing your script header.

Pinning a version with @version

Add the directive to the very first non-blank line of your script:

Delta DSL
@version 1
@name "Trend Filter"
@pane "overlay"

@input length = input.int(50, "Length", minval=2, maxval=500)

ma = sma(close, length)
plotLine(ma, color="#F0B90B", width=2)

@version accepts a positive integer. The bare integer form (@version 1) is canonical — it matches the Pine-style //@version=5 precedent and reads cleanly as a release tag rather than a label string. The quoted form (@version "1") is also accepted as a backwards-compatible alias.

Rules:

  • Optional, but recommended for new scripts. If you don't declare @version, the script runs as the default language version (v1 today).
  • Must be first. If present, @version must appear before any executable statement (assignments, calls, if/for/fn blocks). It can sit alongside other directives (@name, @pane, @input) — order among directives doesn't matter, only that it precedes code.
  • One per script. Declaring it twice is rejected.
  • Bounded by what the platform ships. If you write @version 9 but the platform only supports up to v1, the editor surfaces an error: "this script declares Delta DSL v9 but this runtime only supports up to v1". You can't pin to a future version.

Why pin?

Two reasons:

  1. Future-proofing. When v2 ships, your @version 1 script is guaranteed to keep behaving the way it did the day you wrote it. No matter what the platform does to the language, you're frozen on the v1 contract.
  2. Self-documenting. The directive tells anyone reading your script (you in 6 months, an installer, a reviewer) which language contract it was written against. No archeology needed.

What changes between language versions

Three classes of language change have very different impacts on your scripts:

Change typeExamplesEffect on script v1 (no @version or @version 1)
Additive (most common)New stdlib function (tostring, clamp, zscore); new pane mode; new input builderNo effect. Old scripts keep working unchanged. New scripts can use the new feature freely.
Bug fixOff-by-one in a stdlib function; rounding correctionNo effect. Old scripts get the corrected output (which is usually what was intended).
Breaking semanticsA function's output formula changes; an operator's broadcasting rule changesNo effect on @version 1 scripts. The runtime applies v1 semantics for them. New scripts can opt into v2 with @version 2 to get the new behaviour.

The whole point of @version is to let breaking-semantics changes land WITHOUT breaking existing scripts. Today, the platform's policy is "additive only" — bumping to v2 would only happen if a serious quality-of-life issue justifies it, and even then only with overlap (v1 + v2 supported simultaneously).

Worked example

Say one day the platform ships DeltaDSL v2 in which sma() switches its NaN-warmup behaviour from "first length-1 bars are NaN" to "first bar uses just close[0], second bar averages two values, etc.". This is a hypothetical breaking change.

Delta DSL
// Your existing script from 2026:
@name "MA Cross"
ma_fast = sma(close, 10)
ma_slow = sma(close, 50)

// You did NOT declare @version. Default = v1.
// → The platform ships v2.
// → Your script keeps running on v1 semantics:
//      ma_fast is NaN for bars 0..8, value at bar 9.
// → Nothing changes for you. Your charts paint identically.

If you want the v2 behaviour:

Delta DSL
@version 2       // ← opt-IN to v2 semantics

@name "MA Cross"
ma_fast = sma(close, 10)   // now: bar 0 = close[0], bar 1 = avg of 2, ...
ma_slow = sma(close, 50)

You can also keep both versions of the same indicator side-by-side on the chart — one v1, one v2 — to compare while you decide.

Removing functions (deprecation)

The platform has not removed a public function in production. The deprecation policy is:

  1. Mark the function deprecated in the docs.
  2. Keep it working under all language versions for at least one major-version cycle.
  3. If a future language version retires the function, the directive is what gates that — older @version keeps the function working; newer @version replaces it with a no-op or alias.

In practice, the safer path is to leave deprecated helpers in place forever and document the preferred alternative — the cost of keeping a few extra stdlib entries is zero, and breaking installer scripts has a real human cost.

What does NOT need @version to be safe

  • New stdlib functions. Adding tostring, zscore, time helpers, etc. doesn't touch any existing semantics. Old scripts keep working; you can adopt the new function whenever you edit.
  • New drawing primitives. Same reasoning — new function names, no conflict with old ones.
  • New input.* builders or @pane modes. Additive. Old scripts that don't reference them are unaffected.

You only need @version to gate semantic changes — and those are rare by policy.

Hard caps and runtime safety

Three execution-time caps protect every script from runaway loops. They've remained the same throughout DeltaDSL's evolution and won't change without an RFC:

CapValueWhat blows it
MAX_DRAW_RECORDS50,000Drawing more than 50k records per evaluation.
state.loopBudget5,000,000The total iteration budget across for loops.
state.callDepthLimit64Recursive function calls.

If a script blows a cap, the runtime ejects with an explicit error message identifying which cap fired. Bumping a cap is treated as a language-level decision and requires an RFC backed by a benchmark.

Migration checklist when editing a shared script

If you maintain a script others depend on, run through these before saving a major edit:

  • Did inputs rename or change type? If yes, downstream users may lose their parameter customisations. Prefer adding new inputs over renaming existing ones.
  • Did @pane change? Switching from "overlay" to "below" (or vice versa) silently moves the indicator. Either keep the pane or document the move.
  • Did alertcondition titles change? The user's wired alerts are matched by name (or title if name is omitted). Renaming a title is renaming the alert — wired alerts may detach.
  • Did persistent slot names change? Old slots may go orphan. Rename intentionally or keep names.
  • Compile log clean? Open the dialog after saving and check there are no warnings.
  • Does the script work on multiple symbols / timeframes? The chart you tested might mask edge cases.

A reasonable safety pattern for large rewrites is to fork the script into a new id (Save As → new name), iterate on the fork until it's right, then ask installers to migrate manually. The original script keeps working unchanged for everyone who installed it.

Anti-patterns to avoid

Don't store state across versions in params

Params are user-overridable UI prefs (see Inputs). They are NOT a place for migration data, internal state, or "compatibility flags". A new version cannot read the previous version's params; the user's stored param overrides simply re-apply to the new version's @input defaults.

Don't treat @input as a private internal config

Anything you declare as @input becomes user-visible in the indicator settings panel. If you need an internal constant, declare it as a normal variable in the script body, not as @input.

Don't rely on undocumented behavior

If a runtime quirk isn't documented, it might change. Use only the documented behaviour. If you discover something useful in the engine that isn't documented, file an issue so it can be specced.

Don't skip version pin testing

Before activating a rollback for production users, test the rolled-back version on a chart in the same conditions (symbol, timeframe, indicators stacked) where the regression was reported. Versions store source, not snapshots of every interaction the chart has — there's a small chance the regression report points at an interaction with an updated paired indicator, not the script under test.

Next

  • Recipes — full script examples that follow the migration discipline above.
  • Pitfalls — failure modes to recognise during edits.
  • Chart Terminal overview — how the user interacts with versions in the dialog.