From 9b923e8280a28521ba03c0b99e0f9b4789b67acb Mon Sep 17 00:00:00 2001 From: Mark Chernyshev Date: Mon, 31 Aug 2026 15:47:44 +0200 Subject: [PATCH 1/3] MILAB-6848: use the sc chain domain letter directly cdr3-spectratype now uses the scClonotypeChain domain letter everywhere, with the workflow's TCR swap gone and the selector kept in classic order (Alpha=B, Beta=A). The block was already picking the right chain, so the real bug was just a wrong letter in the exported label; the cost is that existing single-cell TCR blocks re-run on the opposite chain. --- .changeset/sc-chain-domain-letters.md | 10 +++++++ model/src/types.ts | 1 + ui/src/utils.ts | 9 +++--- workflow/src/main.tpl.tengo | 43 +++++++++++++-------------- 4 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 .changeset/sc-chain-domain-letters.md diff --git a/.changeset/sc-chain-domain-letters.md b/.changeset/sc-chain-domain-letters.md new file mode 100644 index 0000000..0b2f1c4 --- /dev/null +++ b/.changeset/sc-chain-domain-letters.md @@ -0,0 +1,10 @@ +--- +'@platforma-open/milaboratories.cdr3-spectratype.workflow': minor +'@platforma-open/milaboratories.cdr3-spectratype.model': minor +'@platforma-open/milaboratories.cdr3-spectratype.ui': minor +'@platforma-open/milaboratories.cdr3-spectratype': minor +--- + +Single-cell chain selection now uses the `pl7.app/vdj/scClonotypeChain` domain letter everywhere, matching the producers (mixcr-clonotyping, import-vdj-data) and the vj-usage block: A is the more diverse chain — Heavy, Beta, Delta. The workflow no longer inverts A/B for TCRAB/TCRGD, and the selector lists chains in the classic annotation order — Heavy/Light, Alpha/Beta, Gamma/Delta. Emitted spectratype columns are labelled with the chain name instead of the raw letter. + +Unlike the vj-usage fix, this one changes results for existing single-cell TCR blocks: the stored letter is kept but now selects the other chain of the pair, so those blocks re-run on the opposite chain. Re-pick the chain in Settings after upgrading. diff --git a/model/src/types.ts b/model/src/types.ts index 7fb42ea..d375fb2 100644 --- a/model/src/types.ts +++ b/model/src/types.ts @@ -2,6 +2,7 @@ import type { GraphMakerState } from "@milaboratories/graph-maker"; import type { PlRef } from "@platforma-sdk/model"; export type LengthType = "aminoacid" | "nucleotide"; +/** `pl7.app/vdj/scClonotypeChain` domain letter: "A" is Heavy/Beta/Delta, "B" is Light/Alpha/Gamma. */ export type ScChain = "A" | "B"; /** diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 86abdfe..0e37baa 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -37,6 +37,7 @@ export function useScChainOptions( const receptor = axisSpec.domain?.["pl7.app/vdj/receptor"]; + // Listed in classic biological order; values are domain letters, so TCR pairs read B, A. let options: { label: string; value: ScChain }[]; switch (receptor) { case "IG": @@ -47,14 +48,14 @@ export function useScChainOptions( break; case "TCRAB": options = [ - { label: "Alpha", value: "A" }, - { label: "Beta", value: "B" }, + { label: "Alpha", value: "B" }, + { label: "Beta", value: "A" }, ]; break; case "TCRGD": options = [ - { label: "Gamma", value: "A" }, - { label: "Delta", value: "B" }, + { label: "Gamma", value: "B" }, + { label: "Delta", value: "A" }, ]; break; default: diff --git a/workflow/src/main.tpl.tengo b/workflow/src/main.tpl.tengo index 45207c4..545c084 100644 --- a/workflow/src/main.tpl.tengo +++ b/workflow/src/main.tpl.tengo @@ -16,6 +16,13 @@ filter := func(arr, predicate) { return filtered } +// pl7.app/vdj/scClonotypeChain domain letter -> chain name, per receptor. +scChainNames := { + "IG": { "A": "Heavy", "B": "Light" }, + "TCRAB": { "A": "Beta", "B": "Alpha" }, + "TCRGD": { "A": "Delta", "B": "Gamma" } +} + /** Returns a spec for the given type of spectratype. @@ -25,12 +32,12 @@ filter := func(arr, predicate) { vGeneSpec: The spec for the vGene column. cdr3Spec: The spec for the cdr3Length column. lengthType: The type of sequence used as input. - scChain: The chain (only when processing single cell data). + chainName: The chain name (only when processing single cell data). Returns: A spec for the given type of spectratype. */ -getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, scChain) { +getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, chainName) { if type != "v" && type != "cdr3" { ll.panic("Invalid type: " + type) } @@ -44,8 +51,8 @@ getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, scChain) if lengthType != undefined { label = label + " (" + lengthType + ")" } - if scChain != undefined { - label = label + " " + scChain + if chainName != undefined { + label = label + " " + chainName } secondAxis = { column: "vGene", @@ -63,8 +70,8 @@ getSpecs := func(type, abundanceSpec, vGeneSpec, cdr3Spec, lengthType, scChain) if lengthType != undefined { label = label + " (" + lengthType + ")" } - if scChain != undefined { - label = label + " " + scChain + if chainName != undefined { + label = label + " " + chainName } secondAxis = { column: "CDR3", @@ -186,24 +193,14 @@ wf.body(func(args) { cdr3Col := undefined vGeneCol := undefined + chainName := undefined if isSingleCell { - // Map UI chain value to domain chain value based on receptor type - // For TCRAB and TCRGD, the domain mapping is reversed from the UI mapping - // UI: Alpha/Gamma -> "A", Beta/Delta -> "B" - // Domain: For TCRAB: "A" = Beta, "B" = Alpha; For TCRGD: "A" = Delta, "B" = Gamma receptor := datasetSpec.axesSpec[1].domain["pl7.app/vdj/receptor"] - domainChain := args.scChain - if receptor == "TCRAB" || receptor == "TCRGD" { - // Swap A and B for TCR receptors - if args.scChain == "A" { - domainChain = "B" - } else if args.scChain == "B" { - domainChain = "A" - } - } - + namesByLetter := scChainNames[receptor] + chainName = namesByLetter != undefined ? namesByLetter[args.scChain] : args.scChain + predicate := func(col) { - return col.spec.domain["pl7.app/vdj/scClonotypeChain"] == domainChain && col.spec.domain["pl7.app/vdj/scClonotypeChain/index"] == "primary" + return col.spec.domain["pl7.app/vdj/scClonotypeChain"] == args.scChain && col.spec.domain["pl7.app/vdj/scClonotypeChain/index"] == "primary" } cdr3Cols := filter(columns.getColumns("cdr3"), predicate) ll.assert(len(cdr3Cols) == 1, "expected exactly 1 CDR3 column for chain, got %d", len(cdr3Cols)) @@ -348,14 +345,14 @@ wf.body(func(args) { vSpectratypeResult := xsv.importFile( ptResult.getFile("vSpectratype.result.tsv"), "tsv", - getSpecs("v", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, isSingleCell ? args.scChain : undefined), + getSpecs("v", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, chainName), {splitDataAndSpec: true} ) cdr3SpectratypeResult := xsv.importFile( ptResult.getFile("cdr3Spectratype.result.tsv"), "tsv", - getSpecs("cdr3", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, isSingleCell ? args.scChain : undefined), + getSpecs("cdr3", abundanceSpec, vGeneSpec, cdr3LengthSpec, args.lengthType, chainName), {splitDataAndSpec: true} ) From 19cbfdaeb86e37345a362a718957cc3123845f35 Mon Sep 17 00:00:00 2001 From: Mark Chernyshev Date: Mon, 31 Aug 2026 15:49:14 +0200 Subject: [PATCH 2/3] MILAB-6848: bump block-tools to 2.14.3 and tengo-builder to 4.0.23 --- pnpm-lock.yaml | 262 +++++++++++++++++++++++++++++++++++++++++--- pnpm-workspace.yaml | 4 +- 2 files changed, 247 insertions(+), 19 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05c147d..3381864 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,14 +25,14 @@ catalogs: specifier: 1.3.0 version: 1.3.0 '@platforma-sdk/block-tools': - specifier: 2.12.6 - version: 2.12.6 + specifier: 2.14.3 + version: 2.14.3 '@platforma-sdk/model': specifier: 1.80.2 version: 1.80.2 '@platforma-sdk/tengo-builder': - specifier: 4.0.18 - version: 4.0.18 + specifier: 4.0.23 + version: 4.0.23 '@platforma-sdk/test': specifier: 1.80.3 version: 1.80.3 @@ -80,7 +80,7 @@ importers: version: 1.6.0(@types/node@24.5.2)(rollup@4.55.1)(vue@3.5.26(typescript@5.6.3))(yaml@2.8.2) '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.12.6(@types/node@24.5.2) + version: 2.14.3(@types/node@24.5.2) shx: specifier: 'catalog:' version: 0.4.0 @@ -105,7 +105,7 @@ importers: devDependencies: '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.12.6(@types/node@24.5.2) + version: 2.14.3(@types/node@24.5.2) shx: specifier: 'catalog:' version: 0.4.0 @@ -114,7 +114,7 @@ importers: dependencies: '@milaboratories/graph-maker': specifier: 'catalog:' - version: 1.2.4(@milaboratories/pl-model-common@1.47.1)(@platforma-sdk/model@1.80.2)(@platforma-sdk/ui-vue@1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.9.3) + version: 1.2.4(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2)(@platforma-sdk/ui-vue@1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.9.3) '@platforma-sdk/model': specifier: 'catalog:' version: 1.80.2 @@ -133,7 +133,7 @@ importers: version: 1.3.0 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.12.6(@types/node@24.5.2) + version: 2.14.3(@types/node@24.5.2) eslint: specifier: 'catalog:' version: 9.39.2 @@ -211,7 +211,7 @@ importers: devDependencies: '@platforma-sdk/tengo-builder': specifier: 'catalog:' - version: 4.0.18 + version: 4.0.23 '@platforma-sdk/test': specifier: 'catalog:' version: 1.80.3(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.2)) @@ -866,11 +866,20 @@ packages: resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} engines: {node: '>=12.10.0'} + '@grpc/grpc-js@1.14.4': + resolution: {integrity: sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==} + engines: {node: '>=12.10.0'} + '@grpc/proto-loader@0.7.13': resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} engines: {node: '>=6'} hasBin: true + '@grpc/proto-loader@0.8.1': + resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==} + engines: {node: '>=6'} + hasBin: true + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1128,6 +1137,10 @@ packages: resolution: {integrity: sha512-0mS5GQAQYUab0qj1C6+IXafNJhL1ahnhPBmIkLq9VqJU8p9jNZ7KC5ln4/pl0tK47brdPljCLEiLg8IdSa6wFw==} engines: {node: '>=22'} + '@milaboratories/helpers@1.14.5': + resolution: {integrity: sha512-Kiy0g7sEmFQxDwtnmTrdJ8XdUK0IDAB5ZnPCNEbK7lPGHIa+xG3kzfeR4y44QBdrYaK0NwG63D8Fc7dlv9KItg==} + engines: {node: '>=22'} + '@milaboratories/miplots4@1.0.176': resolution: {integrity: sha512-PT2kw5H+YVYR8RNp4SOeP8Pi+sEbnptDQYsQYnwAhjNqfLjBG2kFvDeIXTrULPMW2L1L+hKwdRcEpDrO1ysUKg==} @@ -1165,6 +1178,10 @@ packages: resolution: {integrity: sha512-+0ih1njQn4wvLjGmFKboKFRlWYLrzg7QdRK2aNo8ivTKY16TrsMRCk0GQEt0aPKELnv7QSrsNSjg7e7GSdGMDQ==} engines: {node: '>=22.19.0'} + '@milaboratories/pl-client@3.14.7': + resolution: {integrity: sha512-HIjPfGAYRRD0hbq5YSTkWt5XgBiv+yYtw73EjWLxui8eUEeB2ffdgsvH7IhVx2oXjUOL4p7M4i8PBnmdJ3830w==} + engines: {node: '>=22.19.0'} + '@milaboratories/pl-config@1.8.4': resolution: {integrity: sha512-eBZCIVhl9jRigyJUPURCxH2f2OrU5aToihSrwmTkCOu2rjr1spe4YGMIh9CxPr8Z1GkfQbez3crJGME1+RZrrg==} @@ -1196,18 +1213,27 @@ packages: '@milaboratories/pl-model-backend@1.4.16': resolution: {integrity: sha512-MrJwM3aOzrqr3+GVpS/1Zh/SOH/2tBtoyk/+eE6C7LRjofoip8GIF4be8AgPAM3C+6wTSGrQ5x3CM6A8Ql2HVA==} + '@milaboratories/pl-model-backend@1.4.21': + resolution: {integrity: sha512-Z2z5J8bglslgXXoi1aySi1ho+/d+ylJiuEaRw9XTNuE5iBM3EQPe0rw095pXGOinTlMpmvUcCI2wkVCraUQ1dA==} + '@milaboratories/pl-model-common@1.46.2': resolution: {integrity: sha512-VEeauisApYScvCS8lnK3zpFJ520xuTAodKJmjR8ulHcMrWMyWMfHEdGb7j5OMD0mM/OwTgmQrrJ5eB7Xd+xoOQ==} '@milaboratories/pl-model-common@1.47.1': resolution: {integrity: sha512-5F9I8JvUPRJ2HEzz5MmTio6JFYnhYoMVHfToh2NyXyOyQgAF6jhOzZBOsrEig9NfCPZBRB7v6UjmIK43dG4o/g==} + '@milaboratories/pl-model-common@1.48.0': + resolution: {integrity: sha512-oCVrjFNmjQolb7YWnbSGHnp6GGVm1PhG1lnNABp9lqYjvA2ISjIPQLIo/vT2ca0mqwLrEvbuRqiqSFOg7sQ5tQ==} + '@milaboratories/pl-model-middle-layer@1.30.13': resolution: {integrity: sha512-owQVozyub/6aKgk+yce+9sDK+t0HrpLbnlKJqQjYJahuhoju8j6VMY17AWgvADSDCiI3gnp/Tm/I4Ur6ySs1Cg==} '@milaboratories/pl-model-middle-layer@1.30.7': resolution: {integrity: sha512-rs9x3Ron4ujR/UOdEgB8WUB1SvZ8ZAScT1Av/e4or+iiQ/CzhmK9nqtYamHVwKV+JgwIFbXQwxGvIHDVz955dQ==} + '@milaboratories/pl-model-middle-layer@1.32.0': + resolution: {integrity: sha512-X1iLGgOwzkw8mQ/GfTxfOTJakBJ26np85h5HqUziMbVkFL0SR7wpd7xpgUs6TKVkYNM8viAv3iA2k63Yc5SahQ==} + '@milaboratories/pl-tree@1.13.1': resolution: {integrity: sha512-NtVVUSF9eJA5v5iUEfGgKl0JbG/EIvy+YCsxzCZ1dXG1S3gCNwt01Nj5PkWW7FkDG1IT0ZfwyeO0mVVz2kIv1Q==} engines: {node: '>=22.19.0'} @@ -1240,6 +1266,10 @@ packages: resolution: {integrity: sha512-vZDcIBta7I935VjKk6qGdhpjz2pWXe5CbWv1UUmPrcG8wsunDpWuet8/Gi75N97CpcLDc2LLHd/url9K8ZNpAg==} engines: {node: '>=22.19.0'} + '@milaboratories/ts-helpers@1.8.6': + resolution: {integrity: sha512-ef01tARUl+0Urt3x8HHAByrhYHg4Rnn7WiFyJ+joZFZl1T1pUKTgfB7Zxc8Cn06HIl4i6H7s5OSymjZePIGqfQ==} + engines: {node: '>=22.19.0'} + '@milaboratories/uikit@2.15.16': resolution: {integrity: sha512-HOl1YsPKHpj3izKAs0hEvVtFE8ZbrOnD5KsAqSHxvipQyfAdOLbfB7ak6XPdPKbgsvH5opfMws7usryR6Nlulw==} @@ -1615,6 +1645,10 @@ packages: resolution: {integrity: sha512-MtsYTCNhCCA9drSihSh5uXUY2/qCNai/ekOX2pw2gtzjHPXPqnin1Wzri0z/2N1wOShXodRItBUglALNJFWUsQ==} hasBin: true + '@platforma-sdk/block-tools@2.14.3': + resolution: {integrity: sha512-CD0NPfUoXiJhl6JArC057oCXbqKkJf5HJsmrlZShnh6lKRaIQlibF8VUqbpBi9+PopsGCQ4/s5HnLR7wQoWDzg==} + hasBin: true + '@platforma-sdk/blocks-deps-updater@2.2.0': resolution: {integrity: sha512-p9lBxhFXM9WoRsrJO7dfkiXSK+1m63yIn1sKhBO71eMbhrLMyVYHEOeNf3w5OCdbRF5QsNhXzWuiTmFK3zHFsA==} hasBin: true @@ -1625,8 +1659,11 @@ packages: '@platforma-sdk/package-builder-lib@1.2.0': resolution: {integrity: sha512-AtSzaZ39BGlqcyYtpGREDz+YlGCDdSTQJNV/+NkTyUtoq1ECRO+iMqAX1DSSza1WqylnURX8vKVI2owS/cxntA==} - '@platforma-sdk/tengo-builder@4.0.18': - resolution: {integrity: sha512-nGPB5fE6b9ITFuXVMRakr2MyqsnL4TVE09jZ2X7TCPgb31k4gIhYCT+XU9vBRavBj/U3dt+jSWHkcbX9JvJKYA==} + '@platforma-sdk/package-builder-lib@1.3.0': + resolution: {integrity: sha512-CdBjmNo6E1fBxKYWaXa49L/L2WLURxs2f1TAqxLIZlHRE4DZ6E1TEj3jNNKESWp+/9rwtLkTAzmTzNPrDgz+2Q==} + + '@platforma-sdk/tengo-builder@4.0.23': + resolution: {integrity: sha512-Qkxpg3wuZQOc6KbEL3pifOCdqOH91TaL3jDluVIm5mz3qpx96KO+To8AT/0nLqmvaF7yv8AP22QiyyqduAhGiw==} engines: {node: '>=22'} hasBin: true @@ -1667,12 +1704,21 @@ packages: '@protobufjs/codegen@2.0.4': resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + '@protobufjs/eventemitter@1.1.0': resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + '@protobufjs/fetch@1.1.0': resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} @@ -1688,6 +1734,9 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + '@rolldown/binding-android-arm64@1.0.3': resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5524,6 +5573,10 @@ packages: resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} engines: {node: '>=12.0.0'} + protobufjs@7.6.6: + resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} + engines: {node: '>=12.0.0'} + pump@3.0.2: resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} @@ -7459,6 +7512,11 @@ snapshots: '@grpc/proto-loader': 0.7.13 '@js-sdsl/ordered-map': 4.4.2 + '@grpc/grpc-js@1.14.4': + dependencies: + '@grpc/proto-loader': 0.8.1 + '@js-sdsl/ordered-map': 4.4.2 + '@grpc/proto-loader@0.7.13': dependencies: lodash.camelcase: 4.3.0 @@ -7466,6 +7524,13 @@ snapshots: protobufjs: 7.4.0 yargs: 17.7.2 + '@grpc/proto-loader@0.8.1': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.6 + yargs: 17.7.2 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -7765,12 +7830,38 @@ snapshots: - supports-color - typescript + '@milaboratories/graph-maker@1.2.4(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2)(@platforma-sdk/ui-vue@1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.9.3)': + dependencies: + '@ag-grid-community/core': 32.3.4 + '@milaboratories/helpers': 1.13.7 + '@milaboratories/miplots4': 1.0.176(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0) + '@milaboratories/pf-plots': 1.1.66(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2) + '@platforma-sdk/model': 1.80.2 + '@platforma-sdk/ui-vue': 1.80.4(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.9.3) + '@types/d3-hierarchy': 3.1.7 + '@types/d3-scale': 4.0.9 + '@vueuse/core': 13.5.0(vue@3.5.26(typescript@5.9.3)) + ag-grid-vue3: 34.1.2(vue@3.5.26(typescript@5.9.3)) + canonicalize: 2.1.0 + d3-hierarchy: 3.1.2 + d3-scale: 4.0.2 + vue: 3.5.26(typescript@5.9.3) + transitivePeerDependencies: + - '@milaboratories/pl-model-common' + - d3-dispatch + - d3-path + - d3-scale-chromatic + - supports-color + - typescript + '@milaboratories/helpers@1.13.7': {} '@milaboratories/helpers@1.14.2': {} '@milaboratories/helpers@1.14.4': {} + '@milaboratories/helpers@1.14.5': {} + '@milaboratories/miplots4@1.0.176(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)': dependencies: '@d3fc/d3fc-chart': 5.1.9(d3-array@3.2.4)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(d3-scale@4.0.2)(d3-selection@3.0.0)(d3-shape@3.2.0) @@ -7831,6 +7922,13 @@ snapshots: canonicalize: 2.1.0 lodash: 4.17.23 + '@milaboratories/pf-plots@1.1.66(@milaboratories/pl-model-common@1.48.0)(@platforma-sdk/model@1.80.2)': + dependencies: + '@milaboratories/pl-model-common': 1.48.0 + '@platforma-sdk/model': 1.80.2 + canonicalize: 2.1.0 + lodash: 4.17.23 + '@milaboratories/pf-spec-driver@1.4.21(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@milaboratories/helpers': 1.14.4 @@ -7888,6 +7986,24 @@ snapshots: utility-types: 3.11.0 yaml: 2.8.2 + '@milaboratories/pl-client@3.14.7': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@milaboratories/pl-http': 1.2.4 + '@milaboratories/pl-model-common': 1.48.0 + '@milaboratories/ts-helpers': 1.8.6 + '@protobuf-ts/grpc-transport': 2.11.1(@grpc/grpc-js@1.14.4) + '@protobuf-ts/runtime': 2.11.1 + '@protobuf-ts/runtime-rpc': 2.11.1 + canonicalize: 2.1.0 + denque: 2.1.0 + long: 5.3.2 + lru-cache: 11.2.4 + openapi-fetch: 0.15.0 + undici: 7.16.0 + utility-types: 3.11.0 + yaml: 2.8.2 + '@milaboratories/pl-config@1.8.4': dependencies: '@milaboratories/ts-helpers': 1.8.5 @@ -8001,6 +8117,12 @@ snapshots: canonicalize: 2.1.0 zod: 3.25.76 + '@milaboratories/pl-model-backend@1.4.21': + dependencies: + '@milaboratories/pl-client': 3.14.7 + canonicalize: 2.1.0 + zod: 3.25.76 + '@milaboratories/pl-model-common@1.46.2': dependencies: '@milaboratories/helpers': 1.14.2 @@ -8016,6 +8138,14 @@ snapshots: es-toolkit: 1.39.10 zod: 3.25.76 + '@milaboratories/pl-model-common@1.48.0': + dependencies: + '@milaboratories/helpers': 1.14.5 + '@milaboratories/pl-error-like': 1.12.10 + canonicalize: 2.1.0 + es-toolkit: 1.39.10 + zod: 3.25.76 + '@milaboratories/pl-model-middle-layer@1.30.13': dependencies: '@milaboratories/helpers': 1.14.4 @@ -8032,6 +8162,14 @@ snapshots: utility-types: 3.11.0 zod: 3.25.76 + '@milaboratories/pl-model-middle-layer@1.32.0': + dependencies: + '@milaboratories/helpers': 1.14.5 + '@milaboratories/pl-model-common': 1.48.0 + es-toolkit: 1.39.10 + utility-types: 3.11.0 + zod: 3.25.76 + '@milaboratories/pl-tree@1.13.1': dependencies: '@milaboratories/computable': 2.9.7 @@ -8185,6 +8323,12 @@ snapshots: canonicalize: 2.1.0 denque: 2.1.0 + '@milaboratories/ts-helpers@1.8.6': + dependencies: + '@milaboratories/helpers': 1.14.5 + canonicalize: 2.1.0 + denque: 2.1.0 + '@milaboratories/uikit@2.15.16(typescript@5.9.3)': dependencies: '@milaboratories/helpers': 1.14.4 @@ -8537,6 +8681,31 @@ snapshots: - '@types/node' - aws-crt + '@platforma-sdk/block-tools@2.14.3(@types/node@24.5.2)': + dependencies: + '@aws-sdk/client-ecr-public': 3.859.0 + '@aws-sdk/client-s3': 3.859.0 + '@inquirer/prompts': 7.10.1(@types/node@24.5.2) + '@milaboratories/pl-http': 1.2.4 + '@milaboratories/pl-model-backend': 1.4.21 + '@milaboratories/pl-model-common': 1.48.0 + '@milaboratories/pl-model-middle-layer': 1.32.0 + '@milaboratories/resolve-helper': 1.1.3 + '@milaboratories/ts-helpers': 1.8.6 + '@platforma-sdk/blocks-deps-updater': 2.2.0 + '@platforma-sdk/package-builder-lib': 1.3.0 + canonicalize: 2.1.0 + commander: 15.0.0 + lru-cache: 11.2.4 + mime-types: 2.1.35 + tar: 7.5.2 + undici: 7.16.0 + yaml: 2.8.2 + zod: 3.25.76 + transitivePeerDependencies: + - '@types/node' + - aws-crt + '@platforma-sdk/blocks-deps-updater@2.2.0': dependencies: yaml: 2.8.2 @@ -8568,12 +8737,25 @@ snapshots: transitivePeerDependencies: - aws-crt - '@platforma-sdk/tengo-builder@4.0.18': + '@platforma-sdk/package-builder-lib@1.3.0': dependencies: - '@milaboratories/pl-model-backend': 1.4.16 + '@aws-sdk/client-s3': 3.859.0 + '@aws-sdk/lib-storage': 3.859.0(@aws-sdk/client-s3@3.859.0) + '@milaboratories/resolve-helper': 1.1.3 + archiver: 7.0.1 + undici: 7.16.0 + winston: 3.17.0 + yaml: 2.8.2 + zod: 3.25.76 + transitivePeerDependencies: + - aws-crt + + '@platforma-sdk/tengo-builder@4.0.23': + dependencies: + '@milaboratories/pl-model-backend': 1.4.21 '@milaboratories/resolve-helper': 1.1.3 '@milaboratories/tengo-tester': 1.6.4 - '@milaboratories/ts-helpers': 1.8.5 + '@milaboratories/ts-helpers': 1.8.6 commander: 15.0.0 winston: 3.17.0 @@ -8584,7 +8766,7 @@ snapshots: '@milaboratories/pl-middle-layer': 1.66.3(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2) '@milaboratories/pl-tree': 1.13.1 '@platforma-sdk/model': 1.80.2 - '@vitest/coverage-istanbul': 4.1.9(vitest@4.1.9) + '@vitest/coverage-istanbul': 4.1.9(vitest@4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) vitest: 4.1.9(@types/node@24.5.2)(@vitest/coverage-istanbul@4.1.9)(vite@6.4.1(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) transitivePeerDependencies: - '@bytecodealliance/preview2-shim' @@ -8682,6 +8864,12 @@ snapshots: '@protobuf-ts/runtime': 2.11.1 '@protobuf-ts/runtime-rpc': 2.11.1 + '@protobuf-ts/grpc-transport@2.11.1(@grpc/grpc-js@1.14.4)': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@protobuf-ts/runtime': 2.11.1 + '@protobuf-ts/runtime-rpc': 2.11.1 + '@protobuf-ts/plugin@2.11.1': dependencies: '@bufbuild/protobuf': 2.5.2 @@ -8707,13 +8895,21 @@ snapshots: '@protobufjs/codegen@2.0.4': {} + '@protobufjs/codegen@2.0.5': {} + '@protobufjs/eventemitter@1.1.0': {} + '@protobufjs/eventemitter@1.1.1': {} + '@protobufjs/fetch@1.1.0': dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/float@1.0.2': {} '@protobufjs/inquire@1.1.0': {} @@ -8724,6 +8920,8 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@protobufjs/utf8@1.1.2': {} + '@rolldown/binding-android-arm64@1.0.3': optional: true @@ -11465,6 +11663,22 @@ snapshots: vite: 8.0.16(@types/node@24.5.2)(yaml@2.8.2) vue: 3.5.26(typescript@5.9.3) + '@vitest/coverage-istanbul@4.1.9(vitest@4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.29.0 + '@istanbuljs/schema': 0.1.3 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + tinyrainbow: 3.1.0 + vitest: 4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-istanbul@4.1.9(vitest@4.1.9)': dependencies: '@babel/core': 7.29.0 @@ -11477,7 +11691,7 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 tinyrainbow: 3.1.0 - vitest: 4.1.9(@types/node@24.5.2)(@vitest/coverage-istanbul@4.1.9)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.2)) + vitest: 4.1.9(@types/node@24.5.2)(@vitest/coverage-istanbul@4.1.9)(vite@6.4.1(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -13272,6 +13486,20 @@ snapshots: '@types/node': 24.5.2 long: 5.3.2 + protobufjs@7.6.6: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 24.5.2 + long: 5.3.2 + pump@3.0.2: dependencies: end-of-stream: 1.4.4 @@ -13967,7 +14195,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.5.2 - '@vitest/coverage-istanbul': 4.1.9(vitest@4.1.9) + '@vitest/coverage-istanbul': 4.1.9(vitest@4.0.16(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.2)) transitivePeerDependencies: - msw diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4ee5940..328e4c9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,9 +12,9 @@ catalog: '@platforma-sdk/workflow-tengo': 6.7.2 '@platforma-sdk/model': 1.80.2 '@platforma-sdk/ui-vue': 1.80.4 - '@platforma-sdk/tengo-builder': 4.0.18 + '@platforma-sdk/tengo-builder': 4.0.23 '@platforma-sdk/package-builder': 3.14.1 - '@platforma-sdk/block-tools': 2.12.6 + '@platforma-sdk/block-tools': 2.14.3 '@platforma-sdk/test': 1.80.3 '@milaboratories/helpers': 1.13.7 '@milaboratories/graph-maker': 1.2.4 From f78778753ee5665b133df383bfbe1d1a1103444d Mon Sep 17 00:00:00 2001 From: Mark Chernyshev Date: Tue, 1 Sep 2026 12:15:13 +0200 Subject: [PATCH 3/3] MILAB-6848: rename the args chain field to force a stale block Since the chain letters changed meaning in the preceding commits, cached results will not match the chain in the settings. A stored "A" ran as an Alpha, but now it reads as a Beta. --- .changeset/sc-chain-domain-letters.md | 4 ++-- model/src/index.ts | 2 +- model/src/types.ts | 2 +- workflow/src/main.tpl.tengo | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.changeset/sc-chain-domain-letters.md b/.changeset/sc-chain-domain-letters.md index 0b2f1c4..504e8a1 100644 --- a/.changeset/sc-chain-domain-letters.md +++ b/.changeset/sc-chain-domain-letters.md @@ -5,6 +5,6 @@ '@platforma-open/milaboratories.cdr3-spectratype': minor --- -Single-cell chain selection now uses the `pl7.app/vdj/scClonotypeChain` domain letter everywhere, matching the producers (mixcr-clonotyping, import-vdj-data) and the vj-usage block: A is the more diverse chain — Heavy, Beta, Delta. The workflow no longer inverts A/B for TCRAB/TCRGD, and the selector lists chains in the classic annotation order — Heavy/Light, Alpha/Beta, Gamma/Delta. Emitted spectratype columns are labelled with the chain name instead of the raw letter. +Single-cell chain selection now uses the `pl7.app/vdj/scClonotypeChain` domain letter, matching the producers (mixcr-clonotyping, import-vdj-data) and the vj-usage block: A is the more diverse chain — Heavy, Beta, Delta. The workflow no longer inverts A/B for TCRAB/TCRGD, the args field is renamed `scClonotypeChain` to say which convention the letter is in, and emitted columns are labelled with the chain name instead of the raw letter. The selector keeps the classic annotation order, so TCR pairs read Alpha (B), Beta (A). -Unlike the vj-usage fix, this one changes results for existing single-cell TCR blocks: the stored letter is kept but now selects the other chain of the pair, so those blocks re-run on the opposite chain. Re-pick the chain in Settings after upgrading. +Every existing block goes stale and must be re-run, because the args field was renamed. For single-cell TCR that re-run is the point: the stored letter is unchanged but now selects the other chain of the pair, so those blocks switch chain and their exported columns change identity (the chain letter rides in the cdr3Length axis domain). Re-pick the chain in Settings if you wanted the one you had, and update any custom block label naming a chain, since it also becomes the trace label. Bulk and Ig blocks re-run to the same result, with the chain name replacing the letter in Ig labels. diff --git a/model/src/index.ts b/model/src/index.ts index a4717e5..6b915b0 100644 --- a/model/src/index.ts +++ b/model/src/index.ts @@ -13,7 +13,7 @@ export const platforma = BlockModelV3.create(blockDataModel) return { datasetRef: data.datasetRef, lengthType: data.lengthType, - scChain: data.scChain, + scClonotypeChain: data.scChain, customBlockLabel: data.customBlockLabel, }; }) diff --git a/model/src/types.ts b/model/src/types.ts index d375fb2..45f6599 100644 --- a/model/src/types.ts +++ b/model/src/types.ts @@ -30,7 +30,7 @@ export type BlockData = { export type BlockArgs = { datasetRef: PlRef; lengthType: LengthType; - scChain: ScChain; + scClonotypeChain: ScChain; customBlockLabel: string; }; diff --git a/workflow/src/main.tpl.tengo b/workflow/src/main.tpl.tengo index 545c084..195a959 100644 --- a/workflow/src/main.tpl.tengo +++ b/workflow/src/main.tpl.tengo @@ -197,10 +197,10 @@ wf.body(func(args) { if isSingleCell { receptor := datasetSpec.axesSpec[1].domain["pl7.app/vdj/receptor"] namesByLetter := scChainNames[receptor] - chainName = namesByLetter != undefined ? namesByLetter[args.scChain] : args.scChain + chainName = namesByLetter != undefined ? namesByLetter[args.scClonotypeChain] : args.scClonotypeChain predicate := func(col) { - return col.spec.domain["pl7.app/vdj/scClonotypeChain"] == args.scChain && col.spec.domain["pl7.app/vdj/scClonotypeChain/index"] == "primary" + return col.spec.domain["pl7.app/vdj/scClonotypeChain"] == args.scClonotypeChain && col.spec.domain["pl7.app/vdj/scClonotypeChain/index"] == "primary" } cdr3Cols := filter(columns.getColumns("cdr3"), predicate) ll.assert(len(cdr3Cols) == 1, "expected exactly 1 CDR3 column for chain, got %d", len(cdr3Cols))