Skip to content

0040 — The chart type follows the sampling plan

  • Status: accepted (2026-10-06, by the maintainer)
  • Date: 2026-10-06
  • Deciders: maintainer

Context

Until today src/fsmes/services/spc.py drew one chart: individuals and moving range. Every reading was a point, and the limits came from the mean moving range of consecutive readings. That is the right chart when a characteristic is inspected one piece at a time, which is how every characteristic in this product was inspected.

It is the wrong chart when the plan says otherwise. A bottling line that measures five bottles every fifteen minutes is not producing five individuals; it is producing one sample of five, and what a person wants plotted is the sample's mean, with limits built from the within-sample spread. Plotting those five readings as individuals widens the limits with the bottle-to-bottle variation the sampling was designed to hold constant, and the chart stops answering the question it is drawn for: has the process moved?

The two charts are not a preference. Which one is correct is decided by how the pieces were collected, and that is a fact about the plan — so the plan has to be recorded before the chart can be right.

Options considered

Option For Against
The specification carries the sample size; the chart type follows it The plan is recorded where the characteristic is defined, which is where a quality engineer looks for it. One fact decides the chart, the limits, the rules and the capability sigma — nothing can be set inconsistently A specification that changes its plan changes the meaning of its own history. Readings taken under the old plan are set aside rather than redrawn
A setting on the plant: this plant charts X-bar and R One key, no schema change Wrong by construction: two characteristics on the same line are honestly inspected differently, and a plant-wide switch would mis-chart one of them
Infer it from the data — readings that arrive together are a sample Nothing to configure This is inventing production. Five readings a minute apart might be a sample of five or five individuals from a busy station, and the product would be guessing which chart you meant
A chart-type field, chosen by hand, beside the sample size A plant could draw individuals of a sampled characteristic if it wanted Two fields that can disagree, and the disagreement is a chart that lies. The plan already decides it

Decision

A characteristic's sampling plan is QualitySpec.sample_size, and the chart type follows from it. Null or 1 is individuals and moving range, exactly as before. n from 2 to 10 is X-bar and R.

A sample is a record, not a grouping: POST /quality/samples takes exactly n values and writes one quality_checks row per reading — a reading is still a reading, with its own gauge, stamp and value — plus one quality_samples row that ties them together and carries the stamp, the order, the equipment and the gauge. quality_checks.sample_id is nullable and nothing is backfilled: a reading with no sample was not taken as part of one, and inferring otherwise would be inventing a sampling plan nobody wrote down. POST /quality/checks on a sampled characteristic is refused with a sentence that names the sample size.

On a sampled characteristic the plotted point is the sample mean, the limits are X̿ ± A2·R̄, the range chart's are D3·R̄ and D4·R̄, and the sigma capability is computed from is R̄/d2 — the within-sample spread, not the spread of the means. The constants for n = 2…10 are written out in a table.

The four Western Electric rules run on the means, once per sample. A sample is one observation of the process, so it gets one evaluation and at most one non-conformance, and the evidence names the sample and every reading in it. Five bottles whose individual values straddle three sigma and whose mean sits inside the limits fire nothing, which is the point: the process did not move.

A sample range beyond D4·R̄ is its own signal: limits built from a mean range that one inflated sample widened are not limits, so a plant reading only the means would be reading a chart it had quietly broken.

Amended the same day — the range signal is rule 5, and the plant chooses

As first written this record recorded the range signal as rule 0, not one of the four, and held on it everywhere whatever spc_hold_rules said. Hours later the IMR chart landed with rule 5 — a moving range beyond its upper limit — and a product with two numbers for a range beyond its limit would have been a product that asked a plant to say so twice. There is one definition: a range beyond its upper limit is rule 5 on either chart — the moving range on an individuals chart, the sample range on a sampled one — drawn and recorded on every plant per 0036, and raising a hold where the plant's hold_rules includes 5. Rule 0 and always_hold_rules are gone. The bottling pack names rule 5 on its own list, so fill_height there holds on a range as this record intended; a plant that upgrades keeps rules 1 to 4 and sees rule-5 firings recorded, not held.

Consequences

Easier: a line that inspects several pieces at a time gets the chart it should always have had, and its capability figure stops being inflated by bottle-to-bottle variation.

Harder: a characteristic whose plan changes has history under two plans. The readings taken under the old plan are set aside — not padded into short samples, not charted as if they were the new plan — and the chart says how many were set aside and why. That is the honest answer and it is not a comfortable one.

Harder: spc_min_points now counts samples on a sampled characteristic, so a plant that inspects five at a time every fifteen minutes waits five hours for twenty points rather than twenty readings. The number means what it always meant — points on the chart — but the wait is longer, and a plant that wants limits sooner has to say so.

Not decided here: drawing the X-bar and R chart on the screen, and the panel behind a point. web/spc.js says in one sentence that it cannot draw this chart yet rather than drawing means as if they were readings.

Since 2026-10-06 it draws it. kind is what the screen reads: one layout draws both kinds — the sample means with X̿ and X̿ ± A2·R̄ above, the spread inside each sample with R̄, D4·R̄ and D3·R̄ below — and a click on either half opens the n readings behind that sample (GET /quality/spc/{material}/{characteristic}/sample/{sample_id}). The heading, the legend and the sentences under the chart follow kind too, with or without points: this decision made the chart type a property of the data, and a caption that did not follow it would undo that. Nothing above changed.

House rules touched

Never invent production (1): nothing is backfilled, nothing is inferred from how close together readings arrived, and a reading taken outside a plan is set aside rather than fitted into one.

Unknown is not zero (2): a characteristic with no sample size is not a characteristic inspected one at a time by decree — it is one nobody has written a plan for, and the product keeps drawing what it has always drawn for it.

Config, not code, at plant boundaries (4): the plan is in the pack's quality_specs.json and on the Specifications tab, and fsmes pack check refuses a sample size that is not a whole number from 1 to 10, offline.