Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
282 commits
Select commit Hold shift + click to select a range
51fce7e
MILAB-6496: name the tally's preconditions and its reason codes
PaulNewling Aug 17, 2026
d7a1614
MILAB-6496: reduce a set's cells to its verdict, counting the silent …
PaulNewling Aug 17, 2026
5117000
MILAB-6496: tell a narrow majority apart from a tie
PaulNewling Aug 17, 2026
b1f3e56
MILAB-6496: one source of truth for which set a cell belongs to
PaulNewling Aug 17, 2026
9ab626e
MILAB-6496: name a bound competitor beside a negative
PaulNewling Aug 17, 2026
04de545
MILAB-6496: count a set against what it was offered and settled
PaulNewling Aug 17, 2026
c984693
MILAB-6496: strip the single-winner dominance readout
PaulNewling Aug 17, 2026
bfaccbf
MILAB-6496: count a set's silent cells when it contradicts itself
PaulNewling Aug 17, 2026
22a3709
MILAB-6496: declare the run's quality measurements and compute what n…
PaulNewling Aug 17, 2026
c0d50fd
MILAB-6496: QC statuses, parameterised lines, three-level rollup with…
PaulNewling Aug 17, 2026
18794d2
MILAB-6496: close two mutation-surviving gaps in the QC status tests
PaulNewling Aug 17, 2026
9717f6c
MILAB-6496: refuse an against-the-run measurement instead of calling …
PaulNewling Aug 17, 2026
4a66204
MILAB-6496: correct three QC lines against the field's published numbers
PaulNewling Aug 17, 2026
7147d8d
MILAB-6496: add the emit-verdicts entrypoint
PaulNewling Aug 17, 2026
08f7410
MILAB-6496: close four mutation-surviving gaps in the emit-verdicts t…
PaulNewling Aug 17, 2026
75f4b36
MILAB-6496: three spec divergences in the verdict entrypoint
PaulNewling Aug 17, 2026
f5160ae
MILAB-6496: column specs for verdicts, support, set counts and QC
PaulNewling Aug 17, 2026
2249cf8
MILAB-6496: give the two key-only frames a value column
PaulNewling Aug 17, 2026
44c2299
MILAB-6496: make two spec-required facts visible in the output
PaulNewling Aug 17, 2026
190816f
MILAB-6496: acceptance scenarios driven through the real pipeline
PaulNewling Aug 17, 2026
8dabb45
MILAB-6496: a synthetic fixture bed for the awkward panel shapes
PaulNewling Aug 17, 2026
de4b5a4
MILAB-6496: the verdict stage, with every parameter threaded
PaulNewling Aug 17, 2026
01cabdd
MILAB-6496: make the panel part of the QC key, not a column beside it
PaulNewling Aug 17, 2026
6f315f7
MILAB-6496: guard the legacy feature axis where the block tests cannot
PaulNewling Aug 17, 2026
eb285a9
MILAB-6496: model parameters for the reading, replacing dominance
PaulNewling Aug 17, 2026
fcbeb89
MILAB-6496: verdict view and settings for the reading
PaulNewling Aug 17, 2026
b3e3aea
MILAB-6496: a page for the quality measurements and the panel check
PaulNewling Aug 17, 2026
10a910b
MILAB-6496: count a reading only where the cell's own sample offered it
PaulNewling Aug 17, 2026
6c68b3d
MILAB-6496: prose and message fixes from the STE100 review
PaulNewling Aug 17, 2026
e3416f5
MILAB-6496: make five fixtures discriminate what they claim to test
PaulNewling Aug 17, 2026
3caa750
MILAB-6496: changeset and SDK bump for the antigen binding verdicts
PaulNewling Aug 17, 2026
a6e5c4d
MILAB-6496: bump every catalog dependency that can move
PaulNewling Aug 17, 2026
b3c5e71
MILAB-6496: keep the per-cell tables inside the block, per the spec u…
PaulNewling Aug 18, 2026
f077bbd
MILAB-6496: retire the dominance assertions from the test suites
PaulNewling Aug 18, 2026
3928f1a
MILAB-6496: cover the shape a real panel file arrives in
PaulNewling Aug 18, 2026
d500fb8
MILAB-6496: add the two shapes a real panel file arrives in
PaulNewling Aug 18, 2026
cef16bd
MILAB-6496: reshape a generated run's panel into the two real shapes
PaulNewling Aug 18, 2026
6c1f944
MILAB-6496: defer the contending-antigen editor
PaulNewling Aug 18, 2026
6ae316e
MILAB-6496: replace the verdict and quality tables with a punchcard
PaulNewling Aug 18, 2026
1a3535e
MILAB-6496: pair each cell's admissibility with its own row
PaulNewling Aug 18, 2026
b119655
MILAB-6496: stop emit_panel crashing on a ragged CSV row, and give it…
PaulNewling Aug 18, 2026
78c9954
MILAB-6496: a corrupt QC number must never read acceptable
PaulNewling Aug 18, 2026
57a1474
MILAB-6496: make the verdict CLI helper assert the run succeeded
PaulNewling Aug 18, 2026
aa5e51f
MILAB-6496: carry the comparator choice as a machine token, not a sen…
PaulNewling Aug 18, 2026
38e2d65
MILAB-6496: build the punchcard with createPlDataTableV3, and show ev…
PaulNewling Aug 18, 2026
2d900a3
MILAB-6496: join the panel to the counts through the sample-label map
PaulNewling Aug 18, 2026
f183c6d
MILAB-6496: draw not-bound as a faint dot instead of a blank cell
PaulNewling Aug 18, 2026
3e98d3a
MILAB-6496: green bound, red not-bound, grey unreliable, empty never-…
PaulNewling Aug 18, 2026
8761569
MILAB-6496: make the punch actually paint, and only on antigen columns
PaulNewling Aug 18, 2026
ee7a53b
MILAB-6496: a legend for the punchcard, and cells that explain their …
PaulNewling Aug 18, 2026
0acf8d1
MILAB-6496: render the punch explanation instead of relying on a nati…
PaulNewling Aug 18, 2026
222b5c1
MILAB-6496: label a merged identity with the names it declared, and s…
PaulNewling Aug 18, 2026
1e43015
MILAB-6496: truncate long antigen headers, with a toggle for the full…
PaulNewling Aug 18, 2026
cd825c1
MILAB-6496: export the declarations that hold of each identity
PaulNewling Aug 18, 2026
5403187
MILAB-6496: close the spec-compliance review's code findings
PaulNewling Aug 18, 2026
205fbae
MILAB-6496: point the settings comments at the page that still exists
PaulNewling Aug 19, 2026
b49f1d2
MILAB-6496: a punchcard narrowing that matches nothing means all, not…
PaulNewling Aug 19, 2026
1db8e25
MILAB-6496: name the baseline once, and stop it colliding with the co…
PaulNewling Aug 19, 2026
1a0b086
MILAB-6496: derive the baseline source instead of hiding it behind a …
PaulNewling Aug 19, 2026
4321c35
MILAB-6496: stop two controls meaning something while showing nothing
PaulNewling Aug 19, 2026
6864305
MILAB-6496: say which of the two control settings changes the numbers
PaulNewling Aug 19, 2026
e90e264
MILAB-6496: name the antigens a barcode disagreed about under per-tag…
PaulNewling Aug 19, 2026
8768e10
MILAB-6496: drop the header checkbox, sort the card by name, and capi…
PaulNewling Aug 19, 2026
2ec91f6
MILAB-6496: fix the high-baseline tooltip, and say what the threshold…
PaulNewling Aug 19, 2026
fabe960
MILAB-6496: stop the first tooltip calling a count a verdict
PaulNewling Aug 19, 2026
8102fc0
MILAB-6496: warn when a sample-keyed panel has no sample column
PaulNewling Aug 19, 2026
fd828d1
MILAB-6496: show the duplicate-barcode warning, and stop a no-op gest…
PaulNewling Aug 19, 2026
f03b19a
MILAB-6496: hold Run when the panel maps one barcode to two antigens,…
PaulNewling Aug 19, 2026
e093536
MILAB-6496: say what Preview costs, and stop calling the same thing t…
PaulNewling Aug 19, 2026
a93eb23
MILAB-6496: arm the duplicate-mapping gate on the gesture that actual…
PaulNewling Aug 19, 2026
a82cfdc
MILAB-6496: say what the automatic memory sizing actually does
PaulNewling Aug 19, 2026
c8cabbb
MILAB-6496: say how grouped tags combine, and that the floor spares t…
PaulNewling Aug 19, 2026
d66754a
MILAB-6496: stop the baseline warning and the info block saying the s…
PaulNewling Aug 19, 2026
cbc7fc1
MILAB-6496: shorten the CSV tooltip and disambiguate Read 2
PaulNewling Aug 19, 2026
5a57011
MILAB-6496: describe panel-shape quirks by rule, not by provenance
PaulNewling Aug 19, 2026
5bf72f8
MILAB-6496: let the grid narrow the punchcard instead of a picker
PaulNewling Aug 19, 2026
b167ec4
MILAB-6496: changeset for punchcard grid narrowing
PaulNewling Aug 19, 2026
3bd6b6d
MILAB-6496: extract the sample report's log view into its own component
PaulNewling Aug 19, 2026
d5148d4
MILAB-6496: tab the sample report into Visual Report, Quality Checks …
PaulNewling Aug 19, 2026
de6c4d1
MILAB-6496: changeset for the tabbed sample report
PaulNewling Aug 19, 2026
2d726fb
MILAB-6496: show the run's quality report and the panel-versus-reads …
PaulNewling Aug 19, 2026
b8ce106
MILAB-6496: changeset for the Run quality page
PaulNewling Aug 19, 2026
a640d40
MILAB-6496: split the cell artifact so no per-tag grain crosses the b…
PaulNewling Aug 19, 2026
7aa6189
MILAB-6496: changeset for the cell artifact split
PaulNewling Aug 19, 2026
c733f53
MILAB-6496: stop the verdicts output claiming a reader it does not have
PaulNewling Aug 19, 2026
4eaad0e
MILAB-6496: make a Run quality row readable without the column chooser
PaulNewling Aug 19, 2026
6817d15
MILAB-6496: warn when the barcode column holds no sequences
PaulNewling Aug 19, 2026
4f74d0f
MILAB-6496: show punchcard headers in full
PaulNewling Aug 19, 2026
d36e036
MILAB-6496: group tags into identities per tag and sample
PaulNewling Aug 19, 2026
2c886b0
MILAB-6496: drop the sample from the tag-identity linker
PaulNewling Aug 19, 2026
7afca8b
MILAB-6496: correct and simplify the comments this work added
PaulNewling Aug 19, 2026
c35a814
MILAB-6496: carry the grouped-on columns as identity declarations
PaulNewling Aug 20, 2026
c8fa578
MILAB-6496: group on one or more panel columns
PaulNewling Aug 20, 2026
f99e115
MILAB-6496: carry a list of grouping columns in the model
PaulNewling Aug 20, 2026
3260e99
MILAB-6496: offer several grouping columns, barcode among them
PaulNewling Aug 20, 2026
082326c
MILAB-6496: emit the clonotype cell count for the grid
PaulNewling Aug 20, 2026
6f387d8
MILAB-6496: show the could-answer count only where panels differ
PaulNewling Aug 20, 2026
cdb1052
MILAB-6496: unpin the atom version in a comment
PaulNewling Aug 20, 2026
938fd9d
MILAB-6496: validate each grouping column on its own
PaulNewling Aug 20, 2026
425bf0d
MILAB-6496: put the clonotype cell count where the grid can read it
PaulNewling Aug 20, 2026
06a6267
MILAB-6496: say identity where the glossary says identity
PaulNewling Aug 20, 2026
38de6ee
MILAB-6496: refuse a verdict column the panel reader eats as a key
PaulNewling Aug 20, 2026
c979f5b
MILAB-6496: one optional setting per line, and stop calling a control…
PaulNewling Aug 20, 2026
30985c2
MILAB-6496: say what the control mark costs, without naming a block
PaulNewling Aug 20, 2026
8797aef
MILAB-6496: put the baseline thresholds under the baseline
PaulNewling Aug 20, 2026
da2d4ae
MILAB-6496: STE pass over the baseline-source lines
PaulNewling Aug 20, 2026
8eb187e
MILAB-6496: rename the Punchcard page to Explore readout
PaulNewling Aug 20, 2026
e4f38d0
MILAB-6496: build the block on node 22 in CI
PaulNewling Aug 20, 2026
c7ddf80
MILAB-6496: record that the readout's column order already works
PaulNewling Aug 20, 2026
c528037
MILAB-6496: run the python tests on node 22 in CI
PaulNewling Aug 20, 2026
eeda132
MILAB-6496: carry the bound-cell count at each identity
PaulNewling Aug 20, 2026
d1647e3
MILAB-6496: count set-aside cells per clonotype
PaulNewling Aug 20, 2026
f86d9ee
MILAB-6496: correct the punch value's docstring
PaulNewling Aug 20, 2026
d80b157
MILAB-6496: export the bound-cell count as a verdict column
PaulNewling Aug 20, 2026
4dc0627
MILAB-6496: the expansion table, filtered to one clonotype
PaulNewling Aug 20, 2026
dbb01ea
MILAB-6496: open a clonotype's expansion from the readout
PaulNewling Aug 20, 2026
a923bc5
MILAB-6496: name the identity in the expansion, and show could-answer…
PaulNewling Aug 20, 2026
dc33b49
MILAB-6496: give the expansion Ane's column set, and title it rather …
PaulNewling Aug 20, 2026
71b7716
MILAB-6496: fail the expansion loudly when the identity label is missing
PaulNewling Aug 20, 2026
1584240
MILAB-6496: state a clonotype's set-aside cells in its expansion
PaulNewling Aug 20, 2026
ef236ef
MILAB-6496: test the set-aside map where its sparseness can fail
PaulNewling Aug 20, 2026
c5875a9
MILAB-6496: stop the expansion hanging on Loading after a double-click
PaulNewling Aug 20, 2026
1075915
MILAB-6496: show the cells behind a verdict, and drop the never-asked…
PaulNewling Aug 20, 2026
7dab7fc
MILAB-6496: title the expansion with the clonotype's name and cell count
PaulNewling Aug 20, 2026
c1b92ee
MILAB-6496: drop the not-bound column from the expansion panel
PaulNewling Aug 20, 2026
76c3067
MILAB-6496: put cells-that-answered last in the expansion panel
PaulNewling Aug 20, 2026
822de45
MILAB-6496: one large size for every mark on the punchcard
PaulNewling Aug 20, 2026
1e617d9
MILAB-6496: grow the card's marks to 22px, keep the legend at 11px
PaulNewling Aug 20, 2026
d491d0b
MILAB-6496: open the expansion full width, with two tabs
PaulNewling Aug 20, 2026
787993e
MILAB-6496: export the per-cell punchcard
PaulNewling Aug 20, 2026
c9d2deb
MILAB-6496: draw the expansion's by-cell card
PaulNewling Aug 20, 2026
71a766a
MILAB-6496: legend on the by-cell card, and a visible way in to the e…
PaulNewling Aug 20, 2026
ee41c44
MILAB-6496: put the bound count first on the by-cell card
PaulNewling Aug 20, 2026
8fc4afe
MILAB-6496: derive the baseline rung once, and require the role values
PaulNewling Aug 21, 2026
2079245
MILAB-6496: say why an identity's header carries two names
PaulNewling Aug 21, 2026
ef5672b
MILAB-6496: remove the thin-reference branch, and the floor/set wording
PaulNewling Aug 21, 2026
4d70421
MILAB-6496: add the tag-distribution baseline, and fit every baseline…
PaulNewling Aug 21, 2026
23007e4
MILAB-6496: offer the tag-distribution baseline and its two conditions
PaulNewling Aug 21, 2026
1278a17
MILAB-6496: raise the panel rung's member minimum from 8 to 25
PaulNewling Aug 21, 2026
b45f1b8
MILAB-6496: stop the software picking a baseline rung
PaulNewling Aug 21, 2026
dfa016d
MILAB-6496: the baseline is chosen, never derived
PaulNewling Aug 21, 2026
c730f06
MILAB-6496: read against one declared baseline tag, or none
PaulNewling Aug 21, 2026
0706766
MILAB-6496: make the baseline's exemption from the minimum a setting
PaulNewling Aug 21, 2026
ac25ab1
MILAB-6496: align exported column labels to the spec glossary
PaulNewling Aug 21, 2026
6e1192c
MILAB-6496: a self-contradicting panel member blocks the property
PaulNewling Aug 21, 2026
4404403
MILAB-6496: give the synthetic bed a second, measured regime
PaulNewling Aug 21, 2026
24f9e4a
MILAB-6496: recalibrate the shallow tiers against a single-baseline c…
PaulNewling Aug 21, 2026
8a0158a
MILAB-6496: carry the clonotype's cells that read nothing
PaulNewling Aug 21, 2026
b2c4479
MILAB-6496: state a run's settings as the block shows them, in drawer…
PaulNewling Aug 22, 2026
60a3384
MILAB-6496: tighten code comments across the block
PaulNewling Aug 22, 2026
1aa8bcd
MILAB-6496: read the tag-feature panel in the UI
PaulNewling Aug 22, 2026
6514eb1
MILAB-6496: only the sample rolls up
PaulNewling Aug 22, 2026
95a91e8
MILAB-6496: stop measuring saturation and the known answer
PaulNewling Aug 22, 2026
042bf9f
MILAB-6496: measure self-disagreement at the tag only
PaulNewling Aug 22, 2026
257483a
MILAB-6496: pool cells for self-disagreement
PaulNewling Aug 22, 2026
93226a4
MILAB-6496: a comparison is not a line
PaulNewling Aug 22, 2026
08bd4cc
MILAB-6496: no invented lines in the per-sample checks
PaulNewling Aug 22, 2026
d7a55dc
MILAB-6496: changeset for the quality-layer reconciliation
PaulNewling Aug 22, 2026
01e4fa6
MILAB-6496: a tag the reads never show was never asked
PaulNewling Aug 22, 2026
c4d9c1a
MILAB-6496: record the never-asked change in the changeset
PaulNewling Aug 22, 2026
4af20a7
MILAB-6496: a baseline is required, and a run without one does not ha…
PaulNewling Aug 22, 2026
73c9596
MILAB-6496: refuse a run that could establish no baseline
PaulNewling Aug 22, 2026
6a7a70c
MILAB-6496: one value marks the baseline tag
PaulNewling Aug 22, 2026
cf6f439
MILAB-6496: several control feature markers, not one
PaulNewling Aug 22, 2026
5c4186f
MILAB-6496: rewrite the block's comments in Simplified Technical English
PaulNewling Aug 22, 2026
3ad61a7
MILAB-6496: one changeset for the binding-profiling release
PaulNewling Aug 24, 2026
aa460c9
MILAB-6496: restore block/src/index.ts, which is generated
PaulNewling Aug 24, 2026
108e848
MILAB-6496: rework the settings form on Ane's review
PaulNewling Aug 24, 2026
4e8640b
MILAB-6496: retire the panel's own readings as a baseline
PaulNewling Aug 24, 2026
1ef0dc2
MILAB-6496: one settings form, and the cutoff belongs to one rung
PaulNewling Aug 24, 2026
49cc8e5
MILAB-6496: baseline first, and the contending editor goes
PaulNewling Aug 24, 2026
f0a2513
MILAB-6496: the minimum never reaches the baseline tag
PaulNewling Aug 24, 2026
e406949
MILAB-6496: the distribution baseline calls the state its own way
PaulNewling Aug 24, 2026
799e6ad
MILAB-6496: name every setting from the glossary, and group by what i…
PaulNewling Aug 24, 2026
7a2b5c2
MILAB-6496: the sticky-cell controls join the baseline they belong to
PaulNewling Aug 24, 2026
880741a
MILAB-6496: say what the bound score is, and name Cell Ranger
PaulNewling Aug 24, 2026
32fd396
MILAB-6496: agreement as a percentage, and sections named for what th…
PaulNewling Aug 24, 2026
9e6cbd2
MILAB-6496: drop the divider the Panel Settings header replaced
PaulNewling Aug 24, 2026
120c536
MILAB-6496: group tags before naming samples, and name the per-tag op…
PaulNewling Aug 24, 2026
aece3cb
MILAB-6496: show the agreement percent as typed, and pin 0.9 as unset…
PaulNewling Aug 24, 2026
e8db3cf
MILAB-6496: concise settings titles and tooltips, run mode above the …
PaulNewling Aug 24, 2026
ec98f59
MILAB-6496: mark Role column required under a declared baseline
PaulNewling Aug 24, 2026
c360d54
MILAB-6496: read the reagent table's counts from the frame each one i…
PaulNewling Aug 24, 2026
733843f
MILAB-6496: three statuses, and the warn tier the lines already carried
PaulNewling Aug 24, 2026
1d0f6ef
MILAB-6496: re-file the undeclared-barcode line, keep it off the samp…
PaulNewling Aug 24, 2026
b4f3271
MILAB-6496: the fitted background reaches the measurement set
PaulNewling Aug 24, 2026
deb0fd7
MILAB-6496: the run's scores reach the measurement set as a spread
PaulNewling Aug 24, 2026
501a94f
MILAB-6496: put the agreement floor in the label rather than the plac…
PaulNewling Aug 24, 2026
e482906
MILAB-6496: one threshold on the reference reading, and a spread wher…
PaulNewling Aug 24, 2026
ff55616
MILAB-6496: show Run quality again, and draw the three distributions
PaulNewling Aug 24, 2026
5e5b14b
MILAB-6496: one tab per distribution, and stop claiming two rungs at …
PaulNewling Aug 24, 2026
cc263c1
MILAB-6496: split the paired row evenly, and drop "(Background)" from…
PaulNewling Aug 24, 2026
a8cf79b
MILAB-6496: make RUN.md's comparator section agree with the block
PaulNewling Aug 24, 2026
abbb6ed
MILAB-6496: a stale role column no longer greys out Run, and the run …
PaulNewling Aug 24, 2026
a2e945e
MILAB-6496: forward the two distribution frames into the import template
PaulNewling Aug 24, 2026
e6aeca4
MILAB-6496: the metrics step reports its progress instead of holding …
PaulNewling Aug 24, 2026
9246e37
MILAB-6496: show parse's live percent, which was already arriving
PaulNewling Aug 24, 2026
dea4b5e
MILAB-6496: a closed stream is history, not a reading
PaulNewling Aug 24, 2026
af4d03c
MILAB-6496: pin both halves of the live-stream rule
PaulNewling Aug 24, 2026
17fd97a
MILAB-6496: draw the distributions, and say so where one cannot exist
PaulNewling Aug 24, 2026
4dc4fbf
MILAB-6496: one tab per view on the Run quality page
PaulNewling Aug 24, 2026
63721d4
MILAB-6496: name the panel and barcode axes
PaulNewling Aug 24, 2026
a5f02d0
MILAB-6496: a tag reports the samples it was seen in
PaulNewling Aug 24, 2026
c25af35
MILAB-6496: the seen-in denominator is the declared roster
PaulNewling Aug 24, 2026
b2c5edf
MILAB-6496: the seen-in roster is required, not inferred
PaulNewling Aug 24, 2026
4e09057
MILAB-6496: the seen-in test comments state facts
PaulNewling Aug 24, 2026
fed747f
MILAB-6496: a tag reports its disagreement with its siblings
PaulNewling Aug 24, 2026
152a603
MILAB-6496: the sibling majority excludes the tag it judges
PaulNewling Aug 24, 2026
c5696ac
MILAB-6496: a silent tag is not blamed on its siblings
PaulNewling Aug 24, 2026
4ef6355
MILAB-6496: the sibling-disagreement comments state facts
PaulNewling Aug 24, 2026
32bf25c
MILAB-6496: the reagent table, one row per tag and identity
PaulNewling Aug 24, 2026
9c0a1e7
MILAB-6496: name the absent median, and scope the sibling reason to t…
PaulNewling Aug 24, 2026
2a2d5fe
MILAB-6496: scope each reagent row to the samples of its own identity
PaulNewling Aug 24, 2026
84bc938
MILAB-6496: distinguish reagent rows that share a barcode
PaulNewling Aug 25, 2026
bb5df7d
MILAB-6496: correct the expansion table's label-column comment
PaulNewling Aug 25, 2026
f4e01c5
MILAB-6496: correct the expansion table's label-column comment
PaulNewling Aug 25, 2026
7923317
MILAB-6496: drive the sample report and quality tag from the measurem…
PaulNewling Aug 25, 2026
c251da5
MILAB-6496: name the input a valueless measurement is actually missing
PaulNewling Aug 25, 2026
cbd668b
MILAB-6496: state the multi-reference scope boundary as a fact
PaulNewling Aug 25, 2026
e480fd7
MILAB-6496: declare usable-read fraction as deferred, correct false n…
PaulNewling Aug 25, 2026
9938a35
MILAB-6496: trim duplicated rationale from the usable-reads comment
PaulNewling Aug 25, 2026
38787b7
MILAB-6496: count feature barcodes before panel correction
PaulNewling Aug 25, 2026
fde9027
MILAB-6496: address review on the pre-refine feature-count step
PaulNewling Aug 25, 2026
f651e9f
MILAB-6496: put the measurement set on the across-samples table
PaulNewling Aug 25, 2026
275cd82
MILAB-6496: say which table the dataset gate applies to
PaulNewling Aug 25, 2026
2ab75fd
MILAB-6496: drop the unread per-sample QC p-frame and correct two fal…
PaulNewling Aug 25, 2026
30d1de0
MILAB-6496: measure the share of reads in aggregate barcodes
PaulNewling Aug 25, 2026
a6f7887
MILAB-6496: give undeclared barcodes their own table and the line tha…
PaulNewling Aug 25, 2026
1fa3765
MILAB-6496: declare cells detected as a categorical measurement
PaulNewling Aug 25, 2026
c436478
MILAB-6496: name the denominator a zero-read sample is missing
PaulNewling Aug 25, 2026
40fbf74
MILAB-6496: tell a staged panel apart from a dead reagent
PaulNewling Aug 25, 2026
4eea473
MILAB-6496: measure the usable share of antigen reads
PaulNewling Aug 25, 2026
baf664f
MILAB-6496: fix the garbled seen-in-samples description
PaulNewling Aug 25, 2026
21efe0e
MILAB-6496: wire the usable-read fraction to the run
PaulNewling Aug 25, 2026
fbc4826
MILAB-6496: state the cell list and the three unstated exclusions
PaulNewling Aug 25, 2026
265bdeb
MILAB-6496: plot the antigen-count distribution on its own sample
PaulNewling Aug 25, 2026
c2f247a
MILAB-6496: keep a reason out of the detail field
PaulNewling Aug 25, 2026
145cabf
MILAB-6496: split emit_verdicts.py at its three clean seams
PaulNewling Aug 25, 2026
adf4e3a
MILAB-6496: emit each QC line's threshold and route beside its status
PaulNewling Aug 25, 2026
353f8fa
MILAB-6496: split the QC measurements table into per-level sheets
PaulNewling Aug 25, 2026
2f39d7f
MILAB-6496: sort the level sheet values and correct the empty-level note
PaulNewling Aug 25, 2026
39c435a
MILAB-6496: make every QC line and aggregate-barcode knob a CLI flag
PaulNewling Aug 25, 2026
453ffaa
MILAB-6496: thread the four inherited lines through verdict-args
PaulNewling Aug 25, 2026
657becb
MILAB-6496: raise the identity and cell-punch export gates
PaulNewling Aug 25, 2026
eb70498
MILAB-6496: expose the QC lines and aggregate-barcode knobs in the UI
PaulNewling Aug 25, 2026
bedb62c
MILAB-6496: state what this assay never measures, and facet the backg…
PaulNewling Aug 25, 2026
bf4e107
MILAB-6496: let the QC plots take the page's full height
PaulNewling Aug 25, 2026
901c6b8
MILAB-6496: reset the fitted-background plot state so the sample face…
PaulNewling Aug 25, 2026
93699b7
MILAB-6496: show the shipped default in every quality-line setting
PaulNewling Aug 25, 2026
4af4302
MILAB-6496: name the sample and colour the status on the run-quality …
PaulNewling Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/agreement-as-a-percentage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@platforma-open/milaboratories.feature-integration.ui': minor
---

The agreement limit is a percentage, and the form's sections are named for what they hold.

"Share of voting cells that must agree (0–1)" becomes "Voting cells that must agree (%)". A share is a number a reader has to translate, and this one has a floor most readers do not expect: agreement is measured among the cells that answered and the verdict takes the majority, so it can never fall to half or below. The field now runs from 51 to 100 and says why. The data keeps the 0–1 share, so `--min-agreement` and every stored project are unchanged.

Its tooltip now leads with the default, which is **off**: no agreement test runs, a narrow majority stands, and the verdict reports how narrow it was.

The tag-barcode FASTQ dataset is marked required, which `args()` has always enforced. The single-cell V(D)J dataset moves up beside it, so the two dataset inputs sit together above the panel.

Three sections are renamed: a new **Panel Settings** header over the panel file and its columns, **Baseline (background) level** becomes **Baseline (Background) Parameters**, and **The reading** becomes **Threshold Parameters**.
318 changes: 318 additions & 0 deletions .changeset/antigen-barcode-binding-profiling.md

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions .changeset/each-baseline-brings-its-own-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@platforma-open/milaboratories.feature-integration.model': major
'@platforma-open/milaboratories.feature-integration.ui': minor
'@platforma-open/milaboratories.feature-integration.workflow': major
'@platforma-open/milaboratories.feature-integration.per-cell-metrics': major
---

The tag-distribution baseline brings its own rule, and it is the one its method was published with.

`what-plays-the-baseline` fixes that rule: on the raw counts, drop the counts above the 99th percentile, fit a two-component negative binomial mixture, label the higher-median component the signal one, and give each cell the probability that its count belongs to it. A cell reads bound at 0.9 or above. `count-becomes-a-state` is explicit that the score and cutoff of the declared rung do not apply — each baseline brings its own rule, a run selects one baseline, so exactly one rule calls the state.

**This changes every verdict on a distribution-rung run.** The rung previously fitted a kernel density over log2 counts, split it at the deepest trough, took the median of the background as a comparator count, and then handed that count to the declared rung's score. The published method it followed never defines that number, and no run was reading the rule the rung was validated with.

The separation test goes with it, and that is also the spec's instruction rather than an omission. The old fit rejected a tag whose two components did not stand far enough apart, on a threshold this block invented. `what-plays-the-baseline` refuses exactly that: the method assumes two components exist, a tag that bound nothing will be split anyway and its upper slice called signal, no published test replaces the eye, and "a test invented here would be this corpus doing the thing it refuses everywhere else". So the run shows the fit instead of judging it. **A tag nothing bound can now report bound cells.**

`Maximum dip height` is removed from the settings, along with `--distribution-separation`, because a mixture has no trough to measure. The 300-cell condition stays.

`referenceCount` is now null on every distribution-rung row. That rung's comparator is a fitted distribution rather than a reading, and a number there would read as a comparator that served.
13 changes: 13 additions & 0 deletions .changeset/settings-named-from-the-glossary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@platforma-open/milaboratories.feature-integration.ui': minor
---

Every setting is named from the spec glossary, and the form is grouped by what a setting decides.

Seven labels change. "Admissibility gate (baseline UMIs)" and "Min UMIs per barcode" say **unique counts**, which is the glossary's word for a count of distinct molecules. "Minimum cell agreement" becomes "Share of voting cells that must agree", since the glossary's term is a **vote**. "Bound cutoff" becomes "Score at which a cell reads bound", naming the **score** it acts on. "Panel columns that define an identity" becomes "Panel columns that group tags into identities", naming the **grouping**. "High baseline reading" becomes "Line where a baseline reading counts as high", which says it is a line rather than a reading. "Cells needed to fit a tag's own distribution" becomes "Cells a sample needs for this baseline", dropping the method jargon.

Four moves. The sample column joins the other panel-column pickers at the top, where it belongs: it is a panel input, and it was sitting below the baseline among the reading settings. The baseline's own cell condition moves up beside the baseline choice, because it is a condition on that choice. "Optional settings" becomes **The reading** and takes the agreement share, which was alone behind an "Advanced reading settings" accordion that now goes. The "Baseline thresholds" accordion goes too: the gate and the high-reading line both read a cell's own baseline reading, so both exist only under a declared tag, and they now sit in the baseline section with the rung they belong to. A header for two fields that vanish with the rung above them was a section the reader met empty more often than not.

Both of those fields were gated on "not the fitted rung", which also showed them before any baseline was chosen. They are now gated on the declared rung itself.

Three tooltips follow the same words, and the sticky-cell one now says what sticky means and that counting such cells is a measurement while setting them aside is the gate's job.
14 changes: 14 additions & 0 deletions .changeset/the-minimum-never-reaches-the-baseline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@platforma-open/milaboratories.feature-integration.model': major
'@platforma-open/milaboratories.feature-integration.ui': minor
'@platforma-open/milaboratories.feature-integration.workflow': minor
'@platforma-open/milaboratories.feature-integration.per-cell-metrics': minor
---

The minimum count never reaches the baseline tag, and the setting that could make it is gone.

`minimum-count-before-any-reference` states it as a rule rather than a preference: the minimum asks whether a count is evidence of binding, a tag declared to be bound by nothing is never evidence of binding, so the question does not arise for it. A cell's reference reading enters the comparison as it came back, and a small one is the measurement rather than noise to be cleared away.

"Apply the minimum count to the baseline tag" is removed, along with `--minimum-applies-to-baseline` and the `minimumAppliesToBaseline` field. The exemption is now unconditional in `apply_floor`.

**No verdict changes.** The setting was off by default and every rung already built its comparator from raw counts, so the default behaviour is what shipped. What changes is that the other behaviour can no longer be selected. A stored project that had switched it on now runs with the exemption in force, and reports fewer removed readings and fewer emptied cells than it did.
41 changes: 41 additions & 0 deletions .changeset/the-quality-layer-reads-three-statuses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
'@platforma-open/milaboratories.feature-integration.model': minor
'@platforma-open/milaboratories.feature-integration.workflow': minor
'@platforma-open/milaboratories.feature-integration.per-cell-metrics': minor
---

The quality layer reads three statuses, and the reagent table reads the frame each figure is about.

A measurement is now **OK**, **warn** or **alert** and nothing else. One with no line behind it carries no status, and which of the two cases it is reads from the value: a number means nothing judges it, a reason in place of a number means nothing computed it. The row is there either way, and the coverage triple beside it still separates the two.

**Warn is new.** Every inherited line arrives with a warn threshold and an error threshold, and the block held one number per measurement, so each pair was collapsed and a calibrated distinction discarded. Reads per cell at 4,000 read *alerting* and now warns, since one published number gives one boundary. The panel-assigned fraction at 0.49 read *alerting* and now warns, since only a wholly failed sample alerts.

The two thresholds of a line are read independently, because the field warns on a direction and puts error at total failure for three of its four lines.

**The reagent table's figures now come from the right side of the minimum.** Cells with any count and the median count per cell are read from the raw counts, cells called bound from the post-minimum states. The median was taken over bound cells alone, where it could only ever print a number above the cutoff's floor, so a half-degraded reagent showed a healthy figure. Every declared tag keeps a row, so a dead reagent reads as a zero rather than as an absence, and reference tags keep a row whose bound count is empty rather than zero.

**Two measurements changed hands.** The panel-assigned fraction was filed against the *usable antigen reads* row and satisfies the *undeclared barcodes* row: its complement is that quantity exactly, and the line transfers with it. Its status no longer reaches its sample's rollup, because a reagent belongs to the run rather than to any one sample.

**One measurement is new.** The fraction of reads whose cell barcode the chemistry could have produced, warning below 0.75 and alerting below 0.50. The refine-tags report already carried the step it reads. It is the one inherited line with a gradient at both ends rather than a catastrophe, and the reason a third status level exists.

The per-sample quality frame gains a **Valid cell-barcode fraction** column.

**The fitted background now leaves the function that fits it.** Under a population baseline the block fits a two-component mixture per tag and per sample, and kept only the failures. The background component's mean, its share of cells, and the signal mean beside it now reach the measurement set as one tag-level row: the median over the panel's samples that fitted, with the spread and the unfitted count in its detail. It is what a scientist reads to see whether a tag's counts separated at all, and it depends on no cutoff — which matters, because it is read in order to settle one.

Under a declared baseline nothing is fitted, and every row says that rather than going missing.

**The run's scores now have a spread.** The score is computed for every cell and identity, used for one comparison against the cutoff, and was then dropped. It reaches the measurement set as deciles: one figure for the whole run, because the cutoff is one number for the run. A scientist may move that cutoff to where their own run's scores separate, and that licence is unusable unless the scores are in front of them — until now it was set blind.

The measurement carries no line, since a line here would be the block placing the cutoff instead. A population baseline yields a probability rather than a score, and under it the row says so rather than printing a number from the wrong rule. The measurement set gains a **run** grain, which is a grain of one.

**The sticky measurement takes two forms, and the gate decides which.** The block carried two thresholds on a cell's reference reading: the admissibility gate, which sets a cell aside, and a separate observation line defaulted to 100, which counted cells as sticky. Only one exists in the spec. `060-parameter-set` lists seven parameters and a sticky line is not among them, and `290-reference-two-roles` is explicit that *how many are high* needs a high, and only a declared gate supplies one.

So **Sticky cell threshold** is gone from the settings, along with its parameter and its `--high-reference-line` flag. With a gate declared, the cells counted high are the cells it set aside — one number, both jobs. With no gate declared, which is the default, there is no *high* to count and the measurement is the **spread of the reference readings** instead. That spread is what a scientist reads in order to place a gate, and until now a first run offered a count against a line nobody had chosen.

A stored project carrying the old value keeps running; the field is simply no longer read.

**Run quality is visible again, and it draws the three distributions.** The tab was hidden while the quality layer was being re-cut. It returns with the measurement table, the panel-versus-reads check, and a new Distributions section holding the three plots `330-the-quality-readout` asks for: the spread of the run's scores, the reference reading across cells, and the fitted background for each tag.

The deciles and the fitted parameters go out as plottable frames rather than only as detail strings on a measurement row, because a number encoded in a string is one nobody can draw. Both decile sets are taken over the whole run: the cutoff is one number for the run and so is the gate, so each plot shows every cell its number will act on. The backgrounds are emitted at the fit's own `(sample, tag)` grain, since aggregating to the tag would hide a reagent that separated in one sample and not another.

Each plot says so where it cannot exist: a run read against a population baseline has no scores to spread, and one read against a declared baseline tag fits no background.
22 changes: 22 additions & 0 deletions .changeset/the-sample-report-is-the-measurement-set.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@platforma-open/milaboratories.feature-integration.model': minor
'@platforma-open/milaboratories.feature-integration.workflow': minor
'@platforma-open/milaboratories.feature-integration.per-cell-metrics': minor
'@platforma-open/milaboratories.feature-integration.ui': minor
---

A sample's report is its own measurement set, and the Quality tag is that set's rollup.

The Quality Checks tab listed **three** hand-written checks against a threshold the UI held itself, while the software computed **nine** sample-level measurements with their own statuses and their own line provenance. A reader opened a sample, met three rows and a green badge, and concluded the sample had been checked. Six measurements were omitted without saying so, and a list that silently drops what it could not check reads exactly like a list that checked everything and found nothing wrong.

**The tab now lists every sample-level measurement the software declares**, in declaration order, including the ones nothing computed. Each row carries its status where a line stands behind it, its value, and — where there is no value — the reason in place of one. A measurement with no line carries no status and shows an em-dash: there is no fourth status word, and which of the two no-status cases applies is read from the value.

**A blank and a zero are opposite findings, so nothing is ever blank.** Every valueless row states why: the aggregate-barcode fraction because nothing in this block detects aggregates, the sticky measurement because no cell of the sample carries a comparator reading, the read-level fractions because the refine-tags report supplied no step with input reads. The reasons travel in `result_qc.csv` too, on a `reason` column that previously carried only the deferred ones.

**The sample's rolled-up status sits at the top of the list, with its coverage beside it** — how many measurements were judged, how many were computed with no line to judge them against, and how many nothing computed. Whether something is wrong and whether anybody looked are different questions and are answered separately.

**The Main grid's Quality tag is that same rollup and is computed nowhere else.** The UI no longer holds a QC threshold of its own: `PANEL_ASSIGNED_LINE`, `qcChecks`, `qualityStatus` and `QcCheck` are gone, and with them the second copy of a line that could drift from the software's. The tag and the report beside it cannot disagree about one sample.

A measurement whose finding belongs to a reagent rather than to the sample keeps its own status on its row and stays out of the sample's rollup, and its row says so — otherwise a reader meets a status on the page that the tag does not carry.

The verdict step emits `result_qc_by_sample.json` alongside `result_qc.csv`, and the model reads it as `sampleQcReport`. The frame remains the artefact every other reader takes; the report is the same measurements keyed by sample, for a view that holds one sample at a time.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
with:
app-name: 'Block: Feature Integration'
app-name-slug: 'block-feature-integration'
node-version: '20.x'
node-version: '22.x'
gha-runner-label: hz-ubuntu-dind
build-script-name: 'build:dev-local'
build-before-publish-script-name: 'build:release'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mark-stable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
uses: milaboratory/github-ci/.github/workflows/block-mark-stable.yaml@v4
with:
app-name: 'Block: Feature Integration - Mark Stable'
node-version: '20.x'
node-version: '22.x'
npmrc-config: |
{
"registries": {
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with: { node-version: '20.x', cache: 'pnpm' }
with: { node-version: '22.x', cache: 'pnpm' }
- run: pnpm install --frozen-lockfile
- uses: astral-sh/setup-uv@v5
with: { version: "${{ env.UV_VERSION }}" }
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ software/test-data/manual/**
!software/test-data/manual/**/
!software/test-data/manual/**/*.py
!software/test-data/manual/**/*.md
# ...except these dev design/calibration notes — kept on disk, not version-controlled.
software/test-data/manual/design-and-schemas.md
software/test-data/manual/real-data-calibration.md
!software/test-data/manual/assets/whitelist_cells.txt
# Generated runs are ignored WHATEVER their extension. The `*.md` exception above exists for the
# hand-written docs beside the generators, and a generated run carries its own RUN.md report — which
# describes the panel that drove it, and that panel may be a real one supplied from outside the repo.
# Nothing a generator writes is recipe, so nothing under runs/ is tracked.
software/test-data/manual/runs/**

# Local working docs / scratch — not version-controlled
docs/superpowers/
Expand Down
2 changes: 1 addition & 1 deletion block/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"support": "mailto:support@milaboratories.com",
"description": "Assign antigens to single cells from BEAM / LIBRA-seq style feature-barcode reads",
"longDescription": "file:../docs/description.md",
"changelog": "file:../CHANGELOG.md",
"changelog": "file:./CHANGELOG.md",
"tags": [
"single-cell",
"vdj",
Expand Down
6 changes: 3 additions & 3 deletions block/src/agents-extra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
// surface should expose.
//
// In the future this file will host JS functions the MCP runtime
// can execute. The `.d.ts` declarations stay visible to the agent;
// the JS bodies execute in the MCP code-execution context (the
// agent sees the types but not the implementation).
// can execute. The `.d.ts` declarations stay visible to the agent.
// The JS bodies execute in the MCP code-execution context, so the
// agent sees the types but not the implementation.
1 change: 1 addition & 0 deletions model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"check": "ts-builder check --target block-model"
},
"dependencies": {
"@milaboratories/graph-maker": "catalog:",
"@milaboratories/helpers": "catalog:",
"@platforma-sdk/model": "catalog:"
},
Expand Down
Loading
Loading