> ## 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.

# 2026 09 14

<Update label="September 14, 2026" description="A record's section metrics saved as analyses; a timeline of what ran when on a cell; pipeline pages that answer from the pipeline, not its jobs; confidence intervals and sensitivity as pipeline elements">
  ## Every section of a record becomes an analysis

  When a cycling record finishes processing, the platform now measures each
  section it found — a formation, a rate ladder, a pulse train, a stretch of
  cycling — and **saves one analysis per section** against the parent measurement.
  The numbers are stored next to every other extracted feature rather than
  recomputed each time a page asks for them, so they are queryable, plottable and
  downloadable like anything else.

  One analysis per section, not one per record: the sections measure different
  things, and a section that occurs twice — two stretches of cycling at different
  rates — is two results rather than one. Each carries the window it was measured
  over, the metrics it produced, and a note saying why any number is missing.
  Rows the platform derived itself are flagged as such, so a view can separate
  them from analyses a person uploaded.

  Two new families of metric come with it, both of which need the **cell** rather
  than the record:

  * **Specific capacity.** Every capacity gains a counterpart in mA.h.g⁻¹,
    computed from the instance's active mass.
  * **Instance C-rate.** The existing rate divides by the capacity this record's
    window happened to reach; the new one divides by the capacity the cell
    actually holds. On a real rate ladder that is the difference between reading
    0.16 / 0.39 / 0.79 / 1.58 C and reading the 0.2 / 0.5 / 1 / 2 C rungs the
    ladder was run at.

  Both are siblings rather than conversions — the original metrics keep their
  meaning, and a consumer picks the one it means by name.

  ## A timeline of what ran when on a cell

  The cell instance page gains a **Timeline**: every measurement on that cell
  drawn on one shared time axis, docked to the bottom of the viewport as a title
  bar that expands on demand. The grid answers "what exists"; this answers "when,
  and with what gaps" — the question a date-sorted list can only be read one row
  at a time to approximate.

  Runs that don't coincide sit on a single line with the name written inside each
  bar. The solid bar is what the cell actually recorded; where the logged end time
  disagrees, it is drawn beside it — dashed out to a later logged end, or marked
  at an earlier one — because the two disagree often enough to be worth seeing.

  ## Pipeline pages answer from the pipeline, not from its jobs

  A pipeline's progress is now computed server-side and served **per element**, so
  opening a pipeline no longer means the browser fetching a job and its hundred
  children one request at a time. On a large multistart fit that was hundreds of
  requests in a couple of seconds, enough to trip a rate limit with two people on
  the same page. The page now makes a handful.

  Alongside it, a **cancelled pipeline is now terminal on the wire**. Pipelines
  publish `is_terminal` and `is_failed` directly, so a client waiting on one stops
  as soon as it is cancelled instead of polling to its timeout and raising a
  confusing error — and a status added later classifies itself for clients that
  have never heard its name.

  ## Confidence intervals and sensitivity as pipeline elements

  **`LinearConfidenceInterval`** and **`SobolSensitivity`** are now pipeline
  elements you can add after a fit, returning typed results that decode
  client-side with no extra client code. They run as backend jobs, which is the
  only practical home for a Sobol analysis, and reuse the same recipes the
  pipeline library already ships.

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

      * **Drag files straight onto a project's Raw Data table to upload them** — the
        same gesture as a file drive — with a progress bar, a cancel, and new rows
        appearing at the top as they land. The upload page still works as before.
      * The raw data grid **fills the viewport height** instead of stopping at a fixed
        box.

      **Fixes**

      * The **analysis preview reads every analysis the platform writes.** Previews of
        compressed results failed to open; they now decode in the browser.
      * A file that **could not be parsed now stays failed** instead of flipping back
        to "waiting for a cell" with an Attach button that could never succeed — and
        a file whose data cannot be rebuilt fails once, terminally, rather than being
        retried on every cell that is created.
      * **Rows appear during an upload into a project that had none.** The table only
        refreshed while something on screen was already in flight, so a first upload
        showed a progress bar over an empty table.
      * **A measurement created from a raw file now carries its whole header** —
        cycler, channel, operator, instrument, cell label, temperature setpoint,
        software version, export provenance and the test's start time — rather than
        only the procedure name. Nothing downstream can recover it once the file has
        become a measurement, so this was the only place to write it.
      * **Section detection reads its voltage window from substantial loads only.** A
        brief pulse on a depleted cell could define the window, which collapsed a
        whole rate-capability record into one bogus capacity check. Pulse grouping
        also tolerates the cycler's own timing jitter, so a train of 20 pulses stamped
        9.000001 s no longer loses several of them.
      * **Large batches of fits size their submission limits per user**, so one user's
        burst no longer eats the headroom of everyone sharing the platform.
    </Accordion>

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

      * **MSMR site fractions that did not sum to 1 were being accepted silently.**
        Under one of the constraint methods the fit could return fractions summing to
        0.885 or 1.72, which effectively rescales the electrode capacity — a fit could
        look excellent at \~2 mV RMSE while the parameters were unphysical. The
        fractions are now normalized, a post-fit check names the sum and the remedy if
        they still drift, and the stricter constraint method fails loudly rather than
        reporting a bad number.
      * **An MSMR half-cell fit keeps its data's capacity origin.** The capacity
        column was being re-zeroed on the first point, so the model was compared
        against a grid shifted by whatever a cycler export happened to start at, and
        the discarded offset reappeared as a wrong lower excess capacity — a physical
        quantity that feeds downstream state-of-health calculations.
    </Accordion>

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

      * **`raise_on_failure` now covers cancellation**, so a cancelled pipeline raises
        rather than returning as though it had finished.
    </Accordion>

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

      * The **parameterize** templates now read model-vs-data channels off the typed
        result object rather than assembling them by hand, and gain a section on
        **fitting a parameter per objective** — both the fixed-per-objective and the
        fitted-per-objective forms, which the granularity guidance previously
        described without showing how to express.
      * **discover-api** points at the typed series accessor, so an agent reading the
        reference before an operation finds the one-call path rather than only the raw
        two-call read.
    </Accordion>
  </AccordionGroup>
</Update>
