> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ionworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Latest Ionworks Studio updates: Remaining-duration estimates for running tests, spatial profile plots, project webhooks, and cycler hardware and calibration tracking.

<Update label="August 10, 2026" description="Remaining-duration estimates for running tests, spatial profile plots, project webhooks, and cycler hardware and calibration tracking">
  ## Know how much longer a running test has left

  A test already on a channel can now report **how much of it is left**. The
  platform reads the steps the cycler has actually measured, works out exactly
  where the cell has reached in its protocol — which branch it took, how many
  loops it has been through, what its variables hold — and simulates only the
  remainder. Because it starts from where the cell really is rather than where a
  model would have gone, the estimate tracks the physical run instead of an
  idealised one. Duration estimates also now work from the **cell specification**
  alone, so a request that hasn't been assigned a specific cell yet can still be
  estimated.

  ## See what's happening inside the cell

  Simulation results now include **spatial profile plots**. Where a result carries
  a field that varies through the cell rather than a single number per timestep,
  it's rendered as an interactive profile with fixed axes, a proportional time
  control, typed time overlays and marked domain boundaries. You don't pick a plot
  type — whether a variable is scalar or spatial is inferred from the variable
  itself.

  ## Webhooks for your project's events

  Projects can now register **HTTPS webhooks** that fire when something finishes
  or changes. Pipelines and simple pipelines notify you when they reach a terminal
  state — completed, failed or canceled — and nine new cell events cover
  specifications, instances and measurements being created, updated or deleted.
  Webhooks are managed from Project Settings, each delivery is signed so you can
  verify it came from us, retried on failure, and viewable in a delivery log.

  ## Cyclers as real instruments

  A cycler in the Lab now records the details a real instrument has: version,
  serial number, firmware version, and the hostname, IP address and port of its
  control host. On top of that, **calibration and service are tracked** — record
  when a cycler was last calibrated and how often it needs to be, and its next due
  date is derived for you. A cycler that isn't on a calibration schedule reads as
  unknown rather than overdue. Scheduled service windows can be recorded ahead of
  time so the channels are accounted for before the engineer arrives.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * The sidebar gained an **Operate** section holding the Channel Wall and the Test
        Scheduler. The Test Scheduler was previously reachable only by URL.
      * **Plain-English protocol authoring is now available everywhere a protocol is
        created** — the New Protocol page, the new-simulation flow and the interactive
        builder, not just the test scheduler.
      * The Agent now **collapses code and printed output by default**, showing the
        answer rather than the transcript. Plots and errors always stay visible.
      * Activity log entries can now be **filtered to a single project**.

      **Fixes**

      * Fixed live results going missing or stale on some jobs — result metadata could
        silently fail to upload while the job still reported success.
      * Quick-model simulations now create their model inside the project you ran them
        from, so it appears in the project's model lists instead of being invisible
        everywhere.
      * Fixed a crash on the pipeline detail page when a parameter had no value.
      * Deleting a measurement that a channel booking or an active plan depends on now
        explains which record is blocking it, and stored files are only removed once the
        deletion itself succeeds.
      * Fixed the "learn more" link on the Protocol Simulator upload screen, which 404ed.
      * Render failures are now reported to our error tracking, so crashes get fixed
        rather than going unnoticed.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * Fits now report **per-objective timing and the cost of a parameter sweep**, so the
        work a fit represents can be measured before it is scheduled.
      * **Breaking:** `MultiCost` now takes one input form across both the schema and
        pipeline packages. The previously documented `{cost: weight}` mapping never
        worked in the schema package — a list of cost/weight records is now the single
        supported form on both sides.

      **Fixes**

      * Invalid `DataFit` options are now **rejected when you submit**, with a message
        naming the unrecognised option, instead of failing minutes into the run with an
        unexplained internal error.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * New **`client.search`** sub-client for global search across projects, models, cell
        specifications, instances, measurements, materials, studies, optimizations and
        pipelines.
      * **`client.protocol` now covers the whole protocol lifecycle** — listing, fetching,
        finding by name, creating, updating and deleting saved protocols — instead of only
        validation and conversion.
      * New clients for **project webhooks** and for **cycler hardware, calibration and
        scheduled service**.

      **Fixes**

      * Fixed `get_plot_data` returning a 404 for every request — the client was calling an
        address that did not exist.
      * An API URL configured with a trailing slash no longer produces a doubled slash and
        a 404.
    </Accordion>

    <Accordion title="Protocol Simulator">
      **Improvements**

      * A protocol's position can now be **recovered from a real cell's measured steps** —
        which step it is on, its loop counters, subroutine stack, variables and cycle
        number. Combined with the existing resume support, a simulation can pick up exactly
        where a physical test has got to.
    </Accordion>

    <Accordion title="Skills">
      **Improvements**

      * New **Search** skill covering global search across the platform from the SDK.
      * New **Manage protocols** skill covering the full saved-protocol lifecycle, authoring
        and conversion.
      * **Manage equipment** covers cycler hardware details, calibration tracking and
        scheduled service.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="August 3, 2026" description="Project home dashboard, plain-English protocol authoring, test request detail pages with cycler export, and a Butler-Volmer ECM element">
  ## A home page for every project

  Opening a project used to drop you straight onto its studies list. Projects now
  open on a proper **home page**: an assistant prompt front and centre, a
  **Running now** panel showing pipelines still in flight, a **Pick up where you
  left off** row of your most recently touched studies, pipelines, measurements
  and conversations, and browse tiles with counts for each area of the project.
  Ask a question from the home page and it opens a fresh Agent conversation with
  your question already sent.

  ## Describe a test in plain English

  The Lab test scheduler can now **author a protocol from a description**. Type
  something like "1C/1C cycling for 100 cycles between 4.2 V and 2.5 V with
  10-minute rests" and you get back a validated protocol, ready to request. If
  the description leaves out something that would change what physically runs on
  the channel, you're asked about it rather than having a value guessed; common
  conventions (25 °C ambient, CC-CV taper, rest lengths) are assumed and reported
  back to you. If one of your saved protocols already does the job, it's offered
  instead of a new one. Anything the generator returns is a protocol the platform
  accepts — it's validated before you ever see it.

  ## Test requests get their own page, and export to your cycler

  Planned tests now open on a **detail page** instead of jumping straight into
  the edit form, with the request, its scheduling, and its actions laid out
  separately — so an unscheduled request reads as "not booked yet" rather than a
  half-empty form. From that page you can **export the request's protocol in a
  cycler's native format** — Maccor, Arbin, Neware, BioLogic (BT-Lab) or Novonix
  — preview it, and download it, with no need to re-author the test in vendor
  software. Exports that need more than one file arrive as a single zip.

  ## Charge-transfer kinetics in the ECM

  The Equivalent Circuit Model gained an optional **Butler-Volmer
  charge-transfer element**. Enabling it adds a symmetric charge-transfer
  overpotential driven by a new **Exchange current \[A]** parameter, which
  captures rate-dependent losses that a linear series resistance cannot — nearly
  linear at low current, logarithmic at high current. It's off by default, so
  existing models and simulations are completely unchanged, and the new
  **Charge-transfer overpotential \[V]** output is available when it's on.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * ECM simulation results now show a real **voltage components** plot — open-circuit
        voltage, the series-resistance drop, and one band per RC pair, stacked down to the
        terminal voltage.
      * Measurement views gained a **Filter by Steps or Cycles** selector. Measurements with
        no cycle data — OCV sequences, pulse and DCIR tests, single-pass characterisations —
        now get the full step filter instead of no filter controls at all, and open on Steps.
      * **Channel incident history** — taking a channel out of service now records the
        outage with its cause, category, who reported it and when. A down channel shows a
        callout with the cause, start time and running duration plus a **Return to service**
        action, and the channel page lists its past incidents.
      * Organization admins can manage the **Programs** catalog from a new Programs tab in
        Organization settings — create, rename and delete the programs used to tag planned
        tests.
      * **Protocols are now scoped to a project.** Every project starts with its own
        editable copy of the standard protocols instead of sharing a read-only library, so
        edits in one project can't affect another. Existing projects have been seeded with
        theirs.
      * Uploading measurement time series now returns immediately and **processes in the
        background**, with progress and any processing error visible in the measurement
        list, so large uploads no longer block the request.
      * Revoking an API key now takes effect **immediately** rather than after a delay.

      **Fixes**

      * The Agent widget's **New chat** button now actually starts a blank conversation
        instead of re-opening the last one.
      * Reloading the page mid-reply no longer blanks the earlier messages in an Agent
        conversation — history and the streaming reply are merged rather than one replacing
        the other.
      * Failed API-key authentication no longer echoes the submitted key back in the error
        response; only the key's short prefix is returned.
      * Sorting or filtering cell specifications by **last-updated** no longer errors —
        the field is now stored and returned alongside `created_at`.
      * Transient failures in authentication and membership lookups now report a
        retryable error with a real cause instead of an opaque failure.
      * Program names consisting only of whitespace are now rejected, and program-related
        text meets WCAG contrast requirements.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * Pipeline elements now return **typed result objects** — parameter-estimation,
        optimization, posterior, ensemble, regression and validation results each have
        their own type, reachable from `Pipeline.results`. A new `iwp.analysis` namespace
        collects post-fit analyses, including `LinearConfidenceInterval` and
        `SobolSensitivity`.
      * Interpolant-valued parameters (for example a measured ambient-temperature profile
        supplied per objective) now serialise from a plain dictionary, not just from a
        `pybamm.ParameterValues`. Array-valued parameters round-trip correctly instead of
        being misread as indexed parameters.

      **Fixes**

      * Fixed a memory leak where every fit result held a reference to itself, keeping
        results alive longer than necessary. A parent result whose child happened to hold
        equal parameter values also no longer silently loses that child when copied.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * New clients for **channel incidents** and for the **background measurement upload**
        flow, so an upload can be started and its processing state polled from the SDK.
      * Protocol clients are now project-scoped, matching the per-project protocol change
        above.
      * **Breaking (direct `ionworkspipeline` users only):** `result.optimizer_result`,
        `iwp.OptimizerResult` and `iwp.SamplerResult` are removed in favour of the typed
        result objects above (`OptimizationResult.x` / `.fun` / `.success`,
        `PosteriorResult`, …), and `RegressionResult.result` is now `.results`.
    </Accordion>

    <Accordion title="Data Processing">
      **Improvements**

      * **Power \[W]** is now computed when a measurement is processed and stored with the
        time series, rather than derived on every read. It also stays correct when the
        current sign is auto-corrected during processing.
    </Accordion>

    <Accordion title="Protocol Simulator">
      **Improvements**

      * A protocol can now be **resumed exactly from a captured point** — its position,
        loop counts, variables, cycle number and full physical state — so a run can be
        checkpointed and continued with no rest step and no loss of accuracy.
      * New **protocol authoring warnings** flag common mistakes before you run anything:
        mistyped or cross-scope jumps, missing subroutines, loop conditions that can never
        be satisfied, unreachable steps, voltage limits that contradict the step direction,
        targets outside the safety window, and rests with a current-based end condition.
        These are advisory — nothing that runs today stops running — and can be made
        blocking if you'd rather catch them as errors.
      * Protocols now report their derived **voltage and current limits** — the minimum and
        maximum voltage and the peak charge and discharge currents implied by the steps,
        safety limits and drive cycles.

      **Fixes**

      * Maccor and Novonix exports now use the **earliest** duration end condition rather
        than the last, so a step no longer runs for longer than requested.
      * Fixed a Novonix export writing a structured safety-limit value where a plain number
        was expected.
      * Fixed Arbin loop detection and Neware template handling cases where two
        divergent code paths disagreed.
    </Accordion>

    <Accordion title="Skills">
      **Improvements**

      * **Manage equipment** covers recording and reading a channel's incident history.
      * **Upload data** covers the background upload flow and how to poll a measurement's
        processing state.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="July 27, 2026" description="In-app Agent chat, the Battery Cycler Simulator in projects with a quick model, and Lab test scheduling with programs and watched channels">
  ## Agent chat, built into the app

  A new in-app **Agent** puts a conversational assistant right inside Studio.
  Ask it questions about your data and models, hand it files, and follow along as
  it works — replies stream in as they're generated, and each conversation is
  saved so you can pick it back up later. The Agent runs in an isolated sandbox
  per organization. It's rolling out to a limited set of organizations first and
  will become more widely available over the coming weeks.

  ## Battery Cycler Simulator, now in your projects

  The protocol simulator has moved out of a standalone page and **into your
  projects**, so simulations live alongside the cells, studies, and models they
  belong to. A new **Quick model** option lets you simulate straight away without
  first saving a parameterized model — just pick a chemistry and set a capacity
  and resistance scale inline. Prefer a fitted model? The **From library** toggle
  still runs against any of your saved cell → parameterized-model selections.

  ## Plan and track lab tests

  The Lab view gained a full **test scheduler**. Request a test on a channel and
  the platform validates the protocol up front — an invalid protocol is rejected
  before the request is saved, with a clear message about what's wrong. The
  scheduling dialog shows each channel's live availability and electrical
  ratings as you pick, and you can copy an existing request to seed a new one or
  delete one you no longer need. An optional organization **Programs** catalog
  lets you tag each test with the program it belongs to, visible on the Lab wall
  and planning table.

  Engineers can also **watch a channel** to keep an eye on the test they care
  about and filter the Lab wall down to just those — and because a watch is tied
  to the run currently on the channel, it clears itself automatically the moment
  that test finishes.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * **Battery Cycler Simulator** moved into projects, with a new **Quick model**
        option to simulate without a saved parameterized model.
      * **Lab test scheduler** — request, copy, and delete tests on a channel;
        invalid protocols are rejected on submit with an actionable error.
      * **Programs** — an optional organization catalog for tagging planned tests,
        surfaced on the Lab wall and planning table.
      * **Watch a channel** to follow its live test and filter the Lab wall to your
        watched channels; watches clear automatically when the test completes.
      * A cell specification can now designate a **default parameterized model**; the
        first model created for a spec becomes its default automatically.
      * Measurement details gained a derived **Power \[W]** column
        (voltage × current) selectable in the Time Series tab.
      * The measurement **Differential Analysis** tab now plots **dQ/dV** in addition
        to dV/dQ, switchable from a plot-type dropdown.
      * Material property datasets and analyses now track their **source** — when a
        dataset or analysis was computed from a pipeline or another analysis, you can
        see and click through to where its data came from.
      * Organization admins can **resend an invite** or **send a password reset** to
        a member directly from the Members tab, and adding a member by email now
        distinguishes newly invited from already-registered users.
      * Lab can now be shown or hidden per organization from the admin Navigation
        settings, like the other feature areas.

      **Fixes**

      * A stale browser tab left open across a deploy now **reloads itself** to pick
        up the new version instead of showing a maintenance page.
      * Plot axes, tick labels, and titles are now legible in **dark mode** on
        validation and other charts.
      * Adding a member by email is now case-insensitive, so a differently-cased
        address no longer creates a mismatch.
      * Cycler and channel detail pages no longer error on load for equipment with
        linked child records.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * MSMR half-cell OCP fits now constrain the fitted host-site occupancy
        fractions (Xⱼ) to sum to 1, so the values exported and reused downstream are
        self-consistent.

      **Fixes**

      * A DataFit whose every evaluation is non-finite now **fails with a clear
        error** instead of silently returning the initial guesses as if the fit had
        succeeded.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * Fitted models now persist their **simulation settings** (mesh and solver
        configuration), so a model simulates the same way wherever it's used. The
        `Model` and `ParameterizedModel` clients expose the setting and document how
        to build it from live PyBaMM objects via
        `iws.models.SimulationSettings(...)`.
      * `SimplePipeline.create` now accepts a schema object directly (matching
        `Pipeline.create`), so you no longer need to call `.to_config()` yourself;
        dicts still work unchanged.
      * New `JobClient.get_plot_data(job_id, objective_name, …)` returns a DataFit's
        model-vs-data overlay directly from a completed job.
    </Accordion>

    <Accordion title="Protocol Simulator">
      **Improvements**

      * Impedance (EIS) simulations for the Equivalent Circuit Model now run through
        the same standard PyBaMM machinery as every other model.

      **Fixes**

      * Arbin protocols whose step end conditions aggregate per-channel temperatures
        (e.g. `max(Temperature, …) > 55`) now parse correctly instead of failing with
        an invalid-end-condition error.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="July 20, 2026" description="The new Lab view for tracking cyclers and channels, measurement analyses, and throughput-based degradation in the ECM">
  ## Lab view — see every cycler and channel at a glance

  A new project-scoped **Lab** dashboard shows all of your test equipment
  organized as sites → cyclers → channels, so you can tell at a glance what's
  running where. The Lab wall lists every cycler with a utilization bar and
  per-state channel counts, filterable by site and by amp/volt rating range.
  Drill into a cycler to see its full channel grid, or into a single channel to
  see exactly which cell is on test, its measurement history, and an editable
  out-of-service toggle with notes.

  Each channel's state — **occupied**, **stale** (a run that stopped updating and
  likely needs attention), **free**, or **out of commission** — is derived from
  the measurements linked to it, so the view stays accurate without any live
  telemetry hookup. Channel cards also show an estimated end time for
  still-running tests when one is available. A set of scheduling rules keeps the
  data honest: two runs can't overlap on the same channel, and a channel can't be
  taken out of commission while a test is active.

  The same equipment picture is available programmatically through the new
  `client.lab` sub-client in the Python SDK — ask for overall utilization, list
  free channels (optionally filtered by required ratings), find stale channels
  that need attention, or look up what's on a specific channel, all from a single
  call.

  ## Measurement analyses

  You can now attach **analyses** to a measurement — a named table of extracted
  features (e.g. ECM parameters from EIS, or LLI/LAM degradation points from RPT
  data) stored alongside the raw measurement. The measurement detail page lists a
  measurement's analyses, and each analysis has its own detail page with a data
  preview, downloadable parquet, and a link back to its source measurement.
  Analyses are fully supported in the Python SDK via the new `client.analysis`
  client (create from a polars/pandas DataFrame, list, fetch data, update,
  delete).

  ## Throughput-based degradation in the ECM

  The Equivalent Circuit Model (`iwp.models.ECM`) gained an opt-in **degradation
  capability**: capacity and internal resistance can now evolve as user-supplied
  functions of capacity throughput (the running integral of |current|), so a
  single simulation can capture fade over the course of a run. Both options
  default to constant, so existing ECM configs are unaffected.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * **Global search** now covers cyclers and channels alongside projects,
        studies, and cells — search from anywhere and jump straight to a channel's
        detail page.
      * The cell specification detail page is now organized into three tabs —
        **Measurements**, **Instances**, and **Parameterized models** — each with
        server-side pagination, filtering, and sorting.
      * Organization admins can now **deactivate a member** to suspend their access
        while preserving their membership and role, then reactivate them later. A
        deactivated user sees a dedicated screen with an option to switch to another
        organization.
      * Materials and components are now scoped to the project of the cell
        specification that references them, keeping each project's parameter data
        within its own boundary.

      **Fixes**

      * Sorting the project measurements grid by the **measurement id** column no
        longer makes every row disappear.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * DataFit runs with a Bayesian sampler now return the **full posterior
        sample chain** (per-parameter samples and per-sample costs), not just the
        point estimate, retrievable from the job result.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * New `client.lab` sub-client for point-in-time equipment questions:
        overall utilization, free channels (optionally filtered by required amp/volt
        ratings), stale channels needing attention, and what's on a given channel.
      * New `client.analysis` sub-client for creating, listing, fetching, updating,
        and deleting measurement analyses, serializing DataFrames to and from
        parquet transparently.
      * Submitted pipelines can now reference local data with `file:` and `folder:`
        refs; their contents are read and inlined at submit time, and the same
        inline row cap that applies to raw DataFrames is now correctly enforced for
        these refs (a `file:` pointing at a very large CSV no longer slips through).
      * Raw-data upload now uses the same fast signed-URL flow as regular
        measurement uploads.
    </Accordion>

    <Accordion title="Protocol Simulator">
      **Improvements**

      * Arbin **Power Simulation** drive cycles are now supported — they parse into
        a drive-cycle step that can apply a power (W) waveform rather than current,
        and Arbin cumulative capacity/energy counters are modelled correctly across
        steps.

      **Fixes**

      * Arbin `SetValue` steps that read a last-step quantity (e.g.
        `MV_UD1 = LS_CHAN_Discharge_Capacity`, the "measure C/20 capacity then log
        it" idiom) now resolve correctly at simulation time instead of failing with
        an unresolved-variable error, so the downstream steps that depend on that
        value run.
      * Arbin subschedule steps that happen to share a step index and label with a
        step in the parent schedule no longer collide with a duplicate-step-name
        error; each subschedule's steps are kept distinct while still resolving
        `goto` targets correctly.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="July 13, 2026" description="EIS upload validation in the Python API, and faster pipeline detail page and ECM fits">
  ## EIS upload validation

  `cell_measurement.create(...)` now accepts a `data_type="eis"` hint on the
  measurement dict. When set, `validate_measurement_data` runs a structural
  columns check (always on, requires `Frequency [Hz]`, `Z_Re [Ohm]`,
  `Z_Im [Ohm]`) plus two strict-only, individually-skippable heuristic checks.
  `eis_zim_sign` flags a spectrum whose capacitive band is predominantly
  positive — i.e. the raw `Im(Z)` sign is flipped, with the fix hint
  `Z_Im = -Z_Im`. `eis_impedance_magnitude` catches order-of-magnitude unit
  errors by comparing `min(Z_Re)` against a wide 100× band around a capacity-
  derived estimate — no data fetch, just a sanity check against the supplied
  `rated_capacity`. Both fire only on order-of-magnitude issues (a 1000× unit
  error, a fully flipped sign) so a plausible spectrum is never touched. The
  BioLogic reader now stores the raw imaginary part directly as `Z_Im [Ohm]`
  (negative across the capacitive arc), matching the validator's convention.

  ## Faster pipeline detail page and ECM fits

  The pipeline detail page now loads noticeably faster: large per-element
  data is now loaded lazily on expand instead of upfront, and a slow
  client-side recalculation on every 5s poll tick has been optimized away.
  Separately, pipeline ECM fits got faster and more accurate through several
  internal fit-strategy improvements, with fit quality fully preserved.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * Protocol detail pages now show a **Simulations** section listing every
        simulation that uses the protocol, with the same bulk actions
        (add to study, rerun, delete) available on the parameterized-model
        simulation list.
      * Pipeline **bulk cancel** is now instant. The UI flips immediately with
        optimistic updates, and the slower job-termination work is offloaded to
        run in the background. A single bulk-cancel endpoint replaces the
        per-pipeline loop the frontend was previously doing.
      * The Cycle Filter now clamps `First N`, `Last N`, and `Range` values that
        exceed the number of available cycles: it returns all available cycles
        and shows `Showing all N cycles` in neutral text. Explicit single-cycle
        typos (e.g. `0,50,200` on a 100-cycle measurement) still error, with a
        short, actionable message instead of the previous thousands-of-numbers
        wall of text.
      * **Compute hours** are now a first-class usage dimension alongside
        simulation hours, driven by each org's contract. The account drawer
        renders one meter per configured usage type, gated on a combined loading
        signal with placeholder cards sized to the last-known meter count, so
        the list no longer briefly collapses to zero at load.

      **Fixes**

      * Deleting an organization as a regular Admin (non-super-admin) now returns
        a proper **403 Forbidden**. Previously the delete was silently blocked
        but the endpoint returned 204, giving a false success while the org
        remained in the database.
      * Current sign-convention documentation corrected across backend field
        descriptions, the API discovery endpoint, and the data-upload docs:
        **positive = discharge, negative = charge** (matching the platform's
        actual data and validators). Descriptions of external cycler conventions
        that legitimately differ (e.g. BioLogic / Arbin native positive = charge)
        are unchanged.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * Default population size for the **DE** and **PSO** optimizers is now
        capped at 40. Explicit `population_size` values still bypass the cap;
        only the auto-scaled default changed, and only above the cap.

      **Fixes**

      * Local pipeline runs now match server behavior in three places where they
        had drifted. An OCP interpolant input with only a `data` key is no
        longer rejected as missing metadata. Schema-provided transforms
        (`Log10`, `Log`, `Inverse`, …) are no longer silently dropped. And the
        vacuous multistart guard (`Multiple multistarts are being used, but all
        initial guesses are identical`) that fired as a downstream symptom of
        those drops is gone as a consequence.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * `GET /organizations/current/usage` is now public and returns per-
        dimension usage (`simulation`, `compute`) with limits, so SDK clients
        can read their own quotas without an admin token.
    </Accordion>

    <Accordion title="Data Processing">
      **Improvements**

      * Neware **BTSDA** `.xlsx` files with no explicit `sheets` option now
        auto-select the `record` sheet — where the time series actually lives —
        instead of defaulting to the metadata-only first sheet and failing with
        a missing `Timestamp` error. The previous "you must pass
        `sheets={type: 'name', value: 'record'}`" gotcha is now automatic; files
        without a `record` sheet still fall back to the first-sheet default.

      **Fixes**

      * The Maccor reader now maps **`Cycle C`** — the cumulative cycle counter,
        the true cycle number — to `Cycle from cycler`. Previously it mapped
        `Cycle P`, a program/loop counter that is often `0` and can reset within
        a procedure, so any file containing both columns reported meaningless
        zeros for the cycle number.
    </Accordion>

    <Accordion title="Protocol Simulator">
      **Fixes**

      * Arbin schedules that reference a subschedule (`SubSchedule` step) no
        longer fail parsing with `Duplicate step name found: 'End Test'`. The
        reserved `End Test` terminal is exempted from the duplicate-name check —
        each subschedule keeps its own copy, so any `goto: "End Test"` from any
        scope still resolves at simulation time. Genuinely-named block
        collisions (e.g. a user `Loop_X` appearing in two scopes) still error
        as they should.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="July 6, 2026" description="ECM fitting with regularization and per-cell nominal capacities, ArrayDataFit through the JSON config path, and near-instant measurement deletion">
  ## ECM fitting: regularization and per-cell capacities

  Studio's ECM fit adds two options. A **regularization** setting smooths
  the fitted parameters, and a **per-measurement capacity** lets you set
  a known nominal capacity for each measurement in a fit that spans
  several measurements of the same cell. Capacity fitting is now opt-in,
  and each fitted measurement reports its own capacity.

  ## `ArrayDataFit` through the JSON config path

  `iws.ArrayDataFit` — fitting the same model independently at each value
  of an independent variable (e.g. diffusivity vs. stoichiometry from
  GITT / pulse data, or a parameter vs. temperature) — is now a
  first-class `element_type: "array_data_fit"` end to end. The library
  and schema already supported it, but the config path (the only surface
  the production API sees) previously collapsed it into a plain
  `DataFit`. Array fits now round-trip through config, schema, parser,
  executor, and results serialization, and Studio renders the array-
  valued fitted parameters with a per-parameter scatter view reachable
  from the parameter editor.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * Cell measurement deletion is now near-instant. The per-measurement
        storage layout is deterministic, so the file set to remove is now
        derived from the database record and removed in a single batched
        call, rather than a slow recursive folder listing.
      * Missing or empty step data on a measurement now surface as a clear
        "data missing" error with a re-upload prompt in the UI, distinct
        from transient storage failures (which get a Retry button). Upload
        validation also rejects empty step sets, and the upload rollback
        cleans up orphaned storage files.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * `initial_guess_sampler` on `iws.DataFit` accepts `LatinHypercube` or
        `Uniform` explicitly, and unknown sampler / distribution / prior
        names and stray keys are now rejected at submission time with a
        clear validation error instead of a later runtime crash.

      **Fixes**

      * Configuring an `ArrayDataFit` with a non-number-like `objectives`
        key now surfaces as a clear, actionable configuration error — the
        message is shown, no internal alert — instead of the opaque "An
        unexpected error occurred" path. The same improved error handling
        was applied broadly across data-fit, optimizer, sampler, metrics,
        and transform configuration, so user-correctable mistakes surface
        with an actionable message instead of a generic failure.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * `client.ecm.fit(...)` now accepts a `regularization` option and a
        per-measurement `capacity` on `FitMeasurementRequest` (set on all
        measurements or none); `FitResults.capacity_Ah` reports one value
        per fitted measurement.
      * Submitting an `array_data_fit` element via the JSON config path now
        round-trips end to end (schema → parser → pipeline → results), so
        API clients get the same first-class support the library object
        path already had.

      **Fixes**

      * Documentation examples fixed against the real API surface: the
        resubmit endpoint is `POST /jobs/{pipeline_id}/resubmit` (the
        documented `/pipelines/{pipeline_id}/resubmit` does not exist and
        would 404); the `Model` list response has no `model_type` /
        `config` field (only `client.model.get(id)` returns it);
        `model.create` config uses `type`, not `model_type`; simulate
        protocol examples use named steps + string end conditions;
        `CellMeasurementBundleResponse` is flat (`bundle.name`, not
        `bundle.measurement.name`).
    </Accordion>

    <Accordion title="Skills">
      **Improvements**

      * `run-simple-pipelines` documents the `array_data_fit` element type
        alongside the standard `data_fit`.
      * `manage-cells`: `cell_spec.create` and `create_or_get` examples now
        match `CreateCellSpecificationNested` — a `ratings` object
        (`capacity`, `voltage_min`, `voltage_max` as `{value, unit}`
        Quantities) and per-slot `cathode` / `anode` keys each holding a
        `NestedComponentInput` with a singular `material` dict. The
        previous body used `rated_capacity` / `voltage_range_dc` /
        `components: [...]` fields that do not exist on the model and
        would have failed server-side validation.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="June 29, 2026" description="Faster parallel data fits, name-based measurement lookup, Arbin subschedule/loop support, and clearer model and duplicate errors">
  ## Faster, more reliable parallel data fitting

  Parallel data fitting has been rebuilt on a dedicated execution engine
  that drives optimizers, point evaluation, multistart coordination, and
  progress reporting more efficiently and reliably, with clearer error
  reporting when a fit fails.

  ## Name-based measurement lookup

  `client.resolve_measurement(cell_specification, cell_instance,
    measurement)` resolves a `measurement_id` from human-readable names,
  walking the spec → instance → measurement hierarchy with server-side
  filtering at each level. Scripts no longer need to hand-walk three
  list endpoints and repeat name-matching boilerplate just to get an id.
  `parameterized_model.create_or_get` also lands, giving parameterized
  models the re-runnable create-or-resolve behaviour the other cell
  resources already had.

  ## Clearer errors for model failures and duplicates

  Two opaque "An unexpected error occurred" cases — a fit whose model
  setup needs geometry the user didn't supply, and a custom model with
  no discretisation recipe — now surface as a clear `ModelError` naming
  what failed. Separately, attempting to create a resource that already
  exists now returns a `409 CONFLICT` (with the existing id) instead of
  a `500`, so duplicate-creation attempts fail clearly instead of
  erroring unexpectedly.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * "Save as template" for an optimization now builds the template
        server-side, copying an optimization's saved config directly into
        the template.

      **Fixes**

      * Completed validation/datafit results no longer sporadically show
        "Failed to load plot data." for one of a pair of plots.
      * Cycle-ageing experiments driven directly from stored measurement
        data (`experiment="from data"`) now resolve their measurement
        references correctly, instead of failing later in the run.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * A bare element-wise cost (`SSE`/`MSE`/`RMSE`/`MAE`/`Max`) applied
        to a multi-variable objective now warns when variable lengths
        mismatch — e.g. a model-axis dQ/dV variable against a data-axis
        voltage variable — instead of silently broadcasting to a
        meaningless residual.
      * All 1-D interpolant calculations now accept a `"pchip"`
        interpolator (monotone cubic), the right choice for sparse,
        order-of-magnitude `D(sto)` tables such as half-cell GITT
        per-pulse diffusivity.

      **Fixes**

      * The MSMR half-cell logistic value and derivative are now evaluated
        more robustly, so it can never overflow.
      * Model configuration now rebuilds consistently across execution
        paths, so results agree regardless of how a run was launched.
      * A custom model with serialised geometry/mesh now survives
        multistart runs reliably, fixing cases that previously failed to
        serialize.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * `client.resolve_measurement` and
        `parameterized_model.create_or_get` (both introduced above), plus
        `Model.config` for inspecting a model's stored configuration.
      * Passing a bare pandas/polars DataFrame to a schema `data` field
        (objective `data_input`, `OCPDataInterpolant.data`, the Arrhenius
        calculations' `data`) is now auto-wrapped correctly on
        serialization, instead of failing server-side validation with the
        opaque "Required field 'data' missing".
    </Accordion>

    <Accordion title="Protocol Simulator">
      **Improvements**

      * Arbin protocols now support **subschedules**, **counter loops**,
        and **temperature add-ins**, so schedules that previously crashed
        on upload with "Unsupported step type: SubSchedule" now parse and
        simulate end-to-end.

      **Fixes**

      * BioLogic `.mps` simulation fixes: the loop counter is off by one no
        longer (EC-Lab's `ctrl_repeat` does not count the first pass, so a
        loop now runs `ctrl_repeat + 1` times), plus BCD/EIS support and
        drive-cycle fixes, each reproduced against the reported setting
        files before fixing.
      * User-correctable protocol and configuration errors across the
        Protocol Simulator and pipeline libraries now raise specific,
        actionable error messages instead of generic failures, so they
        surface clearly to the user.
    </Accordion>

    <Accordion title="Data Processing">
      **Improvements**

      * The `process-data` reader gotchas are now documented: selecting the
        `record` sheet for multi-sheet Neware BTSDA `.xlsx` files, and
        current-unit handling — alongside the one-call
        `read.time_series_and_steps` entry point.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="June 22, 2026" description="GITTModel for diffusion-only fits, model downloads for pybamm-only users, full app-URL helpers, and clearer configuration error messages">
  ## GITTModel — a first-class diffusion-only model

  `GITTModel` and `HalfCellGITTModel` are now first-class models in
  `ionworkspipeline` (with matching `ionworks_schema` siblings). GITT
  fits overwhelmingly use the same lightweight model rather than SPMe:
  x-averaged particle diffusion per electrode, a single lumped ohmic
  resistance, fixed OCPs, and no Butler-Volmer kinetics, electrolyte
  dynamics, or thermal effects. Shipping it as a built-in model unblocks
  server-side GITT fits and backs the new half-cell GITT fit template.

  ## Download ionworks-defined models for plain pybamm

  Users who have only `ionworks-schema` / `ionworks-api` — and not the
  licensed `ionworkspipeline` package — can now obtain ionworks-defined
  models (`ECM`, `LumpedSPMR`, the MSMR models, `GITTModel`) in a form
  they can load and run with plain `pybamm`. A new endpoint builds the
  model server-side (where the license lives) and returns a loadable
  serialisation, exposed through the Python SDK.

  ## App-URL helpers for every routed resource

  The SDK's `client.urls` grew from a single `.measurement()` method to
  a full set of web-app link builders — `project`, `model`,
  `parameterized_model`, `optimization`, `pipeline`, `study`,
  `simulation`, `cell_specification`, `cell_instance`, and more — so
  callers never hand-construct `app.ionworks.com` URLs from IDs. The
  helpers are environment-aware, deriving the right host for whichever
  API environment the client is configured against.

  ## Stricter, clearer pipeline configuration

  Most user-driven errors in `ionworkspipeline` now raise a clear
  configuration error instead of a generic one, so configuration
  mistakes — wrong field types, out-of-range values, missing keys — are
  routed to a clear **Configuration error** for the user instead of
  being treated as an internal error. Optimizer configuration is now
  also validated up front: passing options meant for a different
  optimizer now hard-errors at submission time instead of being silently
  ignored.

  <AccordionGroup>
    <Accordion title="Pipeline">
      **Improvements**

      * Objective- and variable-level cost scoping is now driven by a single
        source of truth mapping each objective to the variables it should
        compute. This lets a weighted Wasserstein cost (position-shifting
        dQ/dV) and a per-variable SSE cost be combined over one objective,
        with each cost explicitly scoped to the variables it should consume.
        Electrode balancing gained an option to emit dQ/dV on the model's
        own full-window voltage axis so a weighted Wasserstein comparison
        can align peaks in voltage rather than on the data grid.
      * Strict optimizer-option validation: unknown optimizer option keys
        (and method/option mismatches) are rejected at submission time, and
        unknown constructor options are rejected at runtime with an
        actionable message — the previous behaviour silently ignored them.
        The typed option wrappers for each supported optimizer (CMA-ES, PSO,
        Differential Evolution, XNES, Bayesian Optimization, SOBER, TuRBO)
        remain the recommended way to pass options.
      * Failure handling across the fitting and design-optimization paths is
        now unified onto a single constant, so the two paths can no longer
        drift apart and failures are better-conditioned for surrogate-based
        fits.

      **Fixes**

      * ECM coulomb-count capacity reference is now computed per segment.
        Multi-measurement fits previously accumulated the cumulative
        integral across concatenated segment boundaries, inflating the
        capacity estimate to roughly the sum of per-segment throughputs
        and triggering spurious capacity-sanity-check warnings.
      * A custom `geometry` with symbolic bounds is now correctly restored
        before the simulation is built, instead of arriving in a form that
        choked the solver.
      * Custom models now correctly honour their serialised mesh and
        geometry settings, instead of having them silently rebuilt from
        defaults.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * `client.model.download` / `serialize` (introduced above) return
        ionworks-defined models in a plain-pybamm-loadable form.
      * `client.urls` (introduced above) now builds links for every routed
        resource, environment-aware.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="June 15, 2026" description="Global search with ⌘K, new Bayesian / TuRBO / SOBER optimizers, faster ECM parameterization, inline image previews for file measurements">
  ## Global search across the platform

  A `⌘K` dialog now searches across projects, cell specifications,
  cell instances, cell measurements, pipelines, datafits, simulations,
  optimizations, and parameterized models — with grouped, deep-linked
  results and per-entity-type chip filters. Cell measurements and
  instances are indexed via server-side full-text search, so a partial
  name or id from any of these surfaces lands on the right detail page
  in one keystroke. The search dialog supports filtering and paging
  results server-side.

  ## Bayesian Optimization, TuRBO, and SOBER for parameter estimation

  Three surrogate-based optimizers ship in `ionworkspipeline`:

  * **`BayesianOptimization`** — sequential or batch BO with (log-)Expected
    Improvement and a feasibility-weighted acquisition for constrained
    problems.
  * **`TuRBO`** — trust-region BO with Thompson sampling and constraint
    handling.
  * **`SOBER`** — batch BO via Bayesian quadrature with prior-aware
    importance sampling.

  Each algorithm has a matching `BayesianOptimizationOptions`,
  `TuRBOOptions`, `SOBEROptions` schema, and a new method-selection guide
  in the docs explains when to reach for them over Nelder–Mead, CMA-ES,
  or differential evolution.

  ## ECM parameterization runs as a background job

  Authenticated ECM fits now run as background jobs instead of blocking
  on a synchronous request for 10–60 s. `POST /ecm/fit-from-file` and
  `POST /ecm/fit-from-measurements` return `202 {job_id, status}`; the
  public demo `POST /ecm/fit` stays synchronous so unauthenticated
  example fits still complete in one round-trip. The new `client.ecm`
  Python SDK sub-client covers all three input modes — `fit_from_example`
  (sync), `fit_from_file` and `fit_from_measurements` (returning an
  `EcmFitJob` with `wait_for_completion(...)` polling), plus
  `save_to_project` and `detect_and_read` — and a new `ecm-fitting` agent
  skill walks an agent through picking a mode, running the fit, and
  persisting it as a parameterized model. Solver errors from user data
  now surface as clear, actionable API errors instead of generic
  failures.

  ## Inline image previews for file measurements

  File-type cell measurements used to render a "Visualization not yet
  supported" placeholder; the measurement detail page now fetches every
  attached file, renders images inline (filling the column with a
  click-to-expand lightbox, Esc or click-out to dismiss), and offers a
  download link for any file type. The detail page also no longer fails
  to load for `file` and `properties`-type measurements that don't have
  time series data.

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * Optimizations get a lighter-weight `/optimizations` REST surface
        (list, get, config, metadata, runs, statuses, cancel, resubmit,
        delete), and the optimization list page now polls a lightweight
        status projection every 5 s for faster, more efficient updates. The
        parameterized-model selector also fetches by id rather than scanning
        a larger cache.
      * A project-scoped parameterized model listing lets the
        parameterized-model picker filter to models that belong to the
        current project.
      * Internal indexing helper fields are hidden in the object-fields
        panels so detail views no longer surface implementation details.
      * Datafit results now surface "Validation not supported" as an inline
        alert when applicable, instead of leaving the validation tab blank
        or failing the page.
      * Protocol files that pause or end on a non-error stop condition now
        render a user-correctable inline message; previously the route
        returned a generic error.
      * Errors raised internally are now consistently converted into
        structured error responses across simulation and datafit result
        endpoints.
      * The Universal Cycler Protocol simulator pages now list Arbin and
        Novonix alongside Maccor, Bitrode, and BioLogic as supported cycler
        protocol formats, and the supported-protocols copy is shared between
        both UCP pages so the two stay in sync.
      * Data grid cards now claim an intrinsic width, fixing collapsed-grid
        layout issues when the parent container had no explicit width.
      * Measurement-file uploads to storage now retry on transient errors
        with capped exponential backoff and a clear error once attempts are
        exhausted.
      * Pipeline list and detail views are more robust to out-of-order
        responses, so navigating between pipelines during a slow request no
        longer reverts the view to a stale pipeline. Pipeline-elements
        caching and background polling were also hardened against stale-data
        races.
      * The measurements list resets to page 1 at render time when the
        parent entity changes, removing the flash of stale rows that
        appeared while a new request was in flight.
      * Org-context resolution on hard refresh is now deterministic — the
        active project no longer briefly reverts to the first project in
        the list before the persisted selection loads.

      **Fixes**

      * Dark mode now derives selection, focus, and hover colors from the
        Ionworks brand pink so the focused row, selected nav item, and
        hovered button no longer look muddy against the dark background.
      * Validation plot axis titles render correctly with the latest
        charting library version.
      * The cell card on the cell detail page no longer shows redundant
        metadata fields that duplicated the breadcrumb.
      * The all-measurements page now loads cell specs on cold/direct
        navigation so the spec-name and spec-anode/cathode columns populate
        on first paint instead of only after navigating in from elsewhere.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * `CycleAgeing` defaults to a unified experiment model when an
        experiment is supplied, and compiles by default in that mode.
        Cycling experiments repeat the same handful of steps many times, so
        this collapses build and solve cost, giving a substantial speedup
        for cycle-ageing fits. Pinned to PyBaMM 26.6.1.0.
      * `CycleAgeing` accepts `experiment="from data"`, which builds the
        cycling experiment from the loaded dataset instead of requiring
        the user to restate it.
      * `CycleAgeing` automatically skips storing intermediate-cycle data
        when every requested metric only needs first/last-cycle quantities.
      * `save_at_cycles` on `CycleAgeing` is now auto-derived from the
        requested metric tree when the caller does not pass one
        explicitly — only the cycles that downstream metrics actually
        consume are saved.
      * `ElectrodeBalancing` emits `dQ/dV` curves alongside the existing
        capacity outputs, so weighted costs can be computed directly against
        the dQ/dV peak structure.
      * The `Time` metric accepts a vector of times and reports the metric
        at each one, replacing the previous one-scalar-at-a-time loop in
        callers that needed a few sample points.
      * Solver options can now be passed through to every objective,
        complementing last week's solver options on the constructor side.
      * `SimplePipeline` is a new pipeline mode with a client-side validator
        that rejects configs containing more than one expensive element
        (`DataFit`, `ArrayDataFit`, `Validation`), mirroring the server-side
        guardrail.

      **Fixes**

      * An unsupported preprocessing option is now rejected at config time
        when passed in objective options, matching the runtime ban and
        giving a clearer config-time error.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * `client.electrolyte.transport_from_dataset(dataset_id, forms=, columns=)`
        turns a material property dataset into a `pybamm.ParameterValues`
        of concentration-dependent transport functions ready to hand to
        `iws.direct_entries.DirectEntry(parameters=...)`. Two function
        forms are supported per property: tabulated interpolation
        (default) and a Landesfeind–Gasteiger isothermal exponential fit,
        which stays positive and finite below the lowest measured
        concentration so high-rate DFN solves don't fail when the
        electrolyte depletes.
      * `Quantity` constructors accept PyBaMM-style unit strings and
        normalize them on input, so values copied from a PyBaMM parameter
        set round-trip into and out of cell schemas without a manual unit
        rewrite.
      * `JobClient.get_parameter_trace(job_id)` returns the parameter-
        evaluation trace from an optimization job as a DataFrame for
        inspection or downstream plotting.
      * `client.ecm` (introduced above) is the new ECM sub-client.

      **Fixes**

      * POST and PATCH calls retry on transient connection drops with the
        same capped-backoff policy already used for GETs, removing spurious
        connection errors during long-running uploads against a flaky link.
    </Accordion>

    <Accordion title="Data Processing">
      **Fixes**

      * A constant-current discharge segment with the unsigned mixed-mode
        sign convention no longer mislabels the step direction, and the
        matching CC-discharge classifier no longer reverses the sign on
        file round-trip.
    </Accordion>

    <Accordion title="Protocol Simulator">
      **Improvements**

      * Arbin protocols with a `SIGN(...)` or step-time piecewise-current
        formula now parse correctly and emit the expected step plan; the
        previous parser failed on the conditional with a generic syntax
        error.
      * `human_readable_protocol(...)` renders drive-cycle steps in the
        same shorthand notation the simulator uses elsewhere, instead of
        expanding them inline.

      **Fixes**

      * Pause and end stop reasons now surface as user-correctable inline
        errors instead of being treated as solver failures.
    </Accordion>

    <Accordion title="Skills">
      **Improvements**

      * The `parameterize` skill gains a current-driven fit template and a
        full-cell OCV template, each with an objective / cost decision tree
        so an agent can route an incoming dataset to the right starting
        configuration without re-deriving the choice every time.
      * A new `electrolyte-transport` SDK skill documents the
        `client.electrolyte.transport_from_dataset(...)` workflow,
        including the interpolant vs. Landesfeind-exponential trade-off
        and the `DirectEntry` integration pattern.
      * A new `ecm-fitting` SDK skill walks through the three ECM input
        modes (example, file, measurements) and the save-to-project flow.
      * The `parameterize` skill explains the component/material model and
        the material → cells reverse lookup so an agent can find which cell
        specs reference a given material before editing it.
    </Accordion>
  </AccordionGroup>
</Update>

<Update label="June 8, 2026" description="Brand-aligned theme refresh, semantic zoom on validation plots, new data agent skills, tunable solver options with PyBaMM 26.6 support">
  ## Brand-aligned theme refresh

  Studio now follows the Ionworks brand guide. Pink (`#FF87B7`) drives primary
  CTAs, selection, focus, and active nav/tabs; green-black (`#020E0D`)
  anchors text and neutrals; blue (`#0C68E9`) is reserved for
  hyperlinks so links stay obviously clickable. Corners are sharpened
  to 4px, typography is updated, and JSON viewers, plots, tables, and
  ID chips have been re-skinned to match. All changes are
  theme-level — no underlying behavior changes — so every screen
  lifts at once.

  ## Semantic zoom on pipeline validation plots

  Validation plots now fetch their data on demand and decimate
  server-side based on the visible x-range. Box-zoom into a region
  re-requests denser data for that window; a step-back zoom and a
  full-reset control are stacked in a zoom history.

  ## Tunable solver options with PyBaMM 26.6 support

  `ionworkspipeline` simulations accept new solver options that merge
  over the tuned defaults — e.g. enabling compilation for long
  unified-experiment runs without restating the rest of the solver
  configuration. It threads through every simulation-backed objective.
  The workspace also upgrades to PyBaMM 26.6, with a compatibility layer
  so historical and freshly-generated payloads both decode correctly.

  ## New data agent skills

  Two new agent skills land this release: `build-data-report`
  (multi-section markdown + PDF analysis covering rate capability,
  DCIR, OCV, GITT, entropic, aging, and gap analysis) and
  `validate-data` (a pre-upload gate covering header audit, schema,
  and strict measurement validation).

  <AccordionGroup>
    <Accordion title="Studio">
      **Improvements**

      * Cell-measurements list endpoints have been consolidated and now
        use server-side pagination across project, spec, and instance
        scopes, keeping the main list view fast even for large datasets.

      **Fixes**

      * Deleting a project no longer shows a misleading "The project you
        are trying to access does not exist…" toast. The delete
        confirmation dialog also now requires typing the exact project
        name to enable the **Delete project** button, matching the
        destructive-action pattern used elsewhere.
      * Signed-URL generation for measurement files now retries
        automatically on transient network timeouts, and file listing
        uses a single batched request instead of many parallel calls.
        File downloads also retry when the storage service returns an
        unexpected response under load, and surface exhausted attempts
        as a clear error instead of a generic failure.
      * Cell-instance measurements grid no longer renders the previous
        spec's rows after navigating between specs; the cell selector
        shows a loading skeleton and no longer stalls when an unrelated
        fetch is in flight; the ECM measurement selector switched to
        true server-side pagination instead of loading every project
        measurement into memory.
      * The drive-cycles template list endpoint no longer errors at
        runtime, and now supports standard pagination.
      * Fixed an autocomplete component bug where custom dropdown styling
        options were silently ignored.
    </Accordion>

    <Accordion title="Pipeline">
      **Improvements**

      * A new fit option skips extra pre- and post-fit result-capture
        simulations that aren't surfaced to the user, speeding up
        server-side fits by default; local users keep the previous
        behavior unless they opt in.
      * Configuration errors are now classified more reliably, so the
        **Configuration error** UI path lights up correctly instead of
        being reported as a generic solver error.
    </Accordion>

    <Accordion title="Python API">
      **Improvements**

      * `client.simulation.get_result()` returns a typed `SimulationResult`
        object with `time_series` and `steps` as DataFrames (polars by
        default, pandas when `set_dataframe_backend("pandas")` is active)
        and `metrics` as a plain dict, replacing the previous raw-dict
        response. Callers use attribute access and `.columns` membership
        checks instead of dict-key lookups.
    </Accordion>

    <Accordion title="Skills">
      **Improvements**

      * New `sim-results` SDK skill: fetching time-series and step data
        from a completed simulation, computing SOC via step-boundary
        cumulation, identifying step types by mean current, deriving
        DCIR from pulse steps using an OCV baseline plus onset current,
        and polars-aware plotting conventions.
      * The `parameterize` skill now documents electrode geometry
        (thickness, porosity, particle radius, AM fraction, maximum
        concentration) as a hard **build** requirement for DFN/SPMe — a
        physics-based model cannot be assembled without these structural
        parameters. Geometry lives on the cell spec's components (not on
        a measurement), can come from teardown / metrology / vendor
        datasheet / literature, and FPBM is no longer offered as
        "available" for Tier-2 cells that lack it.
    </Accordion>
  </AccordionGroup>
</Update>

***

<div className="flex justify-between items-center pt-2">
  <span />

  <a href="/changelog/2">Older updates →</a>
</div>
