Skip to content

Releases

0.10.0

Choose a tag to compare

@marcuswendt marcuswendt released this 12 Oct 06:21

Added

  • The timeline is saved with the graph. timeline: { fps, start, end, loop } in the document: Studio's Timeline opens on it and writes it back,
    the runtime uses its fps, and cascade run --frames timeline renders its
    range.

  • run --frames … --mp4 <file> [--crf N] encodes the rendered sequence
    with ffmpeg as H.264 yuv420p at the run's fps, and reports it as video in
    the --json manifest.

  • Stage cache keys Cascade owns. Every Python stage call, Studio or
    headless, is keyed on the stage code (a hash of exec.stages.sources), the
    arguments with input files replaced by their content hash, and the Cascade
    version, and its cache state (hit, miss, off) and key are reported on
    every call: in the server log, in run --json under stages, and in the run
    trace. exec.stages.cache turns on a core result cache that verifies the
    files a stored result names before serving it.

  • Run traces. Every cascade run writes .cascade/runs/<time>-<graph>.json
    with each node's time and its stage calls. run --json works without
    --frames.

  • Seeds. context.seed (from the document's seed and the node id) and
    context.random() in both hosts; cascade run --seed N.

  • cascade parity <graph> [--node] [--tolerance] [--json] cooks headless
    twice and through Studio's executor with caches off and reports every node
    output side by side.

  • Locks and replay. cascade lock <graph> <node> --name <name> records one
    render in locks/<id>.json: the graph as cooked, every prop and seed, input
    file hashes, the project's source code (stored by hash in
    .cascade/objects/), versions and git state, and a fingerprint of every
    output. cascade replay <id> rebuilds that code in a throwaway copy of the
    project and compares fingerprints, without a worktree; --live uses the
    current code instead. cascade lock verify replays every lock and
    cascade locks lists them. All take --json.

  • cascade serve. start | stop | restart | status | logs | install | uninstall [project] runs Studio as a supervised background service: one
    lock, record and log per project under ~/.cascade/serve/, restart with
    backoff after a crash, and a stop that takes the Python worker with it.
    status --json reports port, actual host, pids, uptime, version and worker
    state. install writes and loads a macOS launchd job.

  • Host fallback. When --host does not resolve (a MagicDNS name while
    Tailscale is down) or is not on this machine (EADDRNOTAVAIL), Studio binds
    127.0.0.1 (or --fallback-host, repeatable) instead of crashing, and moves
    back to the preferred host when it returns.

  • Graph editing from the CLI. cascade graph create|read|list, node add|rm, connect, disconnect, set (value, --expr, --key frame=value, --unset), subnet create|move|dissolve and cascade edit for a batch. Each edit is checked with the same code as cascade check, written atomically in the file's own formatting, and leaves a minimal diff: an edit and its inverse leave the file byte-identical. New nodes get stable kebab-case ids and are placed along the graph's flow when wired; wires across a subnet boundary go through named Input/Output nodes. See doc/CLI_REFERENCE.md.

  • --json on every command: one { ok, data, warnings } envelope on stdout.

  • cascade mcp: the same operations as MCP tools over stdio (graph read/list, node add/remove, connect, set, subnets, check, cook, render, export; lock and replay once they land). See doc/MCP.md.

  • The editing API (GraphEditor, readGraph, the operation types) is exported from @field/cascade/runtime.

Changed

  • cascade run --frames --json prints its manifest inside the envelope, as data, rather than at the top level.

Fixed

  • Headless frame renders no longer start Python per stage per frame.
    cascade run uses the project's warm worker for the whole run, and frames
    after the first re-cook only what the frame changed (reuse: "strict";
    --fresh cooks everything). cloud-plots' morph, frames 1–60: 588 s to 5 s,
    byte-identical.

  • An edited stage no longer runs its old code in a live Studio. The warm
    Python worker restarts before the next request when its stage sources
    change, with Python's bytecode cache keyed by the code hash so a quick
    same-length edit is not served from an old .pyc.

  • A failed build keeps the previous dist/. npm run build and
    build:cli build into .dist-staging/ and swap it in only when every step
    passed; the log names the step that failed.

Cascade 0.9.2: shareable editors on phones

Choose a tag to compare

@marcuswendt marcuswendt released this 12 Oct 04:14

Added

  • Exported editors work on phones. The preview is full width at the top,
    with pinch, pan and double-tap to fit. Controls sit in a bottom sheet with
    three snap points and folded groups, sized for thumbs (44 px targets, 16 px
    text, steppers). The header is one row with a ⋯ menu. Safe-area insets and
    100dvh keep everything clear of Safari's toolbar. Exports and scenes use the
    share sheet, with a download as the fallback. Landscape uses a side panel, and
    desktop is unchanged.
  • editor.peek and editor.draft. These choose the controls kept in reach
    at the sheet's lowest point, and the values cooked while a slider is dragged
    (a lower density, say).

Cascade 0.9.1: shareable editors

Choose a tag to compare

@marcuswendt marcuswendt released this 12 Oct 03:58

Added

  • Share from the Inspector. Studio's Inspector has a share toggle per
    parameter that publishes it to the exported editor, with label, group and
    min/max/step fields.

Changed

  • The exported editor cooks in a module worker (editor-worker.js), so a
    slow stage no longer freezes the page while someone drags a slider.

Fixed

  • A fresh tree builds. npm run build compiles the workspace packages
    before vite, and no longer leaves dist/ without a CLI.

0.9.0

Added

  • Shareable editors (0.9.0). cascade export <graph> --out <dir> builds a
    static editor site from a graph's new editor section. It has the published
    parameters (with a UI generated from their types), presets, scene files, one
    export button per declared output, and a read-only inspect view of every
    stage. --plan prints the manifest without building, and
    --deploy vercel deploys the site and checks that Vercel Authentication is
    on.

    The export is an allowlist. It ships the subgraph the outputs need and the
    selected assets, and nothing else. It refuses Python and dynamic nodes,
    wired-in live sources, licensed/, hidden and gitignored files. It scans the
    built site for credentials, local paths and backend URLs.
    cascade freeze snapshots chosen fields of a live source, and
    cascade publish marks a prop as a parameter. Scenes (*.scene.json) open
    in the editor, in Studio (File › Open/Save Scene) and in the CLI
    (cascade scene, cascade run --scene --write), with versioned,
    degrade-gracefully matching.

    The runtime gains run({ reuse: true }), an early cutoff for hosts that
    re-run on every edit, and cascade.core.Frozen. Node definitions gain
    source: "live". See doc/EDITOR_EXPORT.md.

  • Bundle ports. A node can export one bundle port carrying named fields
    instead of a dozen loose outputs. A producer declares fields (name to type)
    and an optional schema; a consumer declares the fields it reads, sees only
    those, and in Studio re-cooks only when one of them changed (per-field
    versions on top of early cutoff). Expressions read a wired bundle's fields
    with ch("format.dpi"). cascade.core.Pick and cascade.core.Pack convert
    between bundles and single values. cascade check reports unknown fields with
    the nearest declared name, and refuses an absolute read of another node's
    bundle. Studio draws a bundle as one neutral port whose hover and Inspector
    list its fields and values. Bundles reach Python stages as dicts. See
    "Bundle ports" in doc/PROJECT_AUTHORING.md.

  • Node previews on the canvas. A project that sets
    settings.nodePreviews: true in cascade.json shows each node's first
    output as a readable preview card on the graph, refreshed per node cook. The
    same settings can carry a workspace used as the first-open dock layout when
    the browser has none saved; a layout that fails to load falls back to the
    built-in one.

  • Groups own their contents. A group frame keeps an explicit
    containedElements list, separate from subnet parenting. Drag the frame
    header to move its members (nested contents move once); drop an item's
    centre inside a frame to add it, outside to remove it, and the smallest
    enclosing frame wins. The target frame highlights before release, and
    membership and positions share one undo step. Deleting a frame leaves its
    contents. View → Clean Up Layout now treats each group as a cluster: it lays
    out members first, fits the frame, then places the groups.

  • The Agent console keeps a Codex conversation, and panels can hand it a
    draft.
    A Codex console resumes the thread id from thread.started, so
    later prompts continue one conversation per sketch as Claude's do; project
    --sandbox flags are normalised for exec resume. Codex item events
    (assistant messages, commands with their output, file changes) render in the
    console. Project panels get api.openAgent(prompt), which opens the Agent
    panel with a draft to review and send, never sending it.

Changed

  • Dragging a node re-renders only what moved. Each node reads its position
    through its own motion store, and the graph is published once when the drag
    ends rather than copied every frame. At 100 nodes a pointer move costs 1.6 ms
    of script instead of 8.9 (5.6 ms of task time instead of 13.1).

  • Node drags apply once per frame. Moving a node or a selection batches
    pointer moves to one update per animation frame instead of one per event.

  • Identical output stops invalidating the graph below it. A node that
    re-cooks and commits the same values (compared structurally; the same
    object reference counts as changed, since it may be mutated in place) keeps
    its output version, and a node stale only because of that upstream re-cook
    is skipped. A node whose own parameters, code or wiring changed always
    cooks, and node.earlyCutoff = false opts a node out. The cook status
    carries the count as cutOff. Python projects get the matching half by
    keying their stage cache on content; cloud-plots does (see its
    runtime/node_cli.py). There, a no-op edit to area-filter-mask now costs
    2 stage calls and 47 ms instead of 13 calls and 866 ms.

  • What a Viewer shows cooks first. A pass ran in plain topological order,
    so a sink nobody was looking at (an export, a plot check, the mockup) could
    run ahead of the node on screen. Now the selected or pinned node of each open
    Viewer and everything it depends on cook first, and the rest of the pass runs
    after, in the same order as before. With no Viewer (the CLI, tests, cook-all)
    nothing changes. On cloud-plots, a Viewer on hatch-curves showed its result
    after 3.4 s instead of 7.4 s on a cold cook.

  • An edit cancels the stage it supersedes. Changing a parameter while a
    long Python stage runs used to wait for that stage, and then for every
    request queued behind it. Now the superseded cook is aborted: its request is
    withdrawn from the worker (dropped if queued, interrupted with SIGINT if
    running, killed if it does not answer), its result is never applied, and the
    pass restarts from the edit. The cook strip shows how many cooks were
    superseded. Measured on cloud-plots, an edit during a 12 s stage now lands in
    0.6 s instead of 10.9 s. Project workers should answer an interrupt with
    {"cancelled": true} to stay warm; see doc/PROJECT_AUTHORING.md.

Fixed

  • Three wire and runtime fixes. Studio's browser-shell adapter sends
    execution options (stdin, timeout, cwd, env) at the top level of the
    /api/shell body again; nested under options the route dropped them. A
    Switch node's index is a document prop, serializes, and re-routes on cook.
    Project CLI instances of one module share a single executor build, so one
    cannot import the .mjs while another rewrites it.