@inflexa-ai/harness 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/data-profile-orientation.d.ts +52 -27
- package/dist/app/data-profile-orientation.js +331 -92
- package/dist/app/data-profile-view.d.ts +70 -0
- package/dist/app/data-profile-view.js +123 -0
- package/dist/app/synthesize-run.js +1 -1
- package/dist/contracts/data-profile.d.ts +276 -25
- package/dist/contracts/index.d.ts +3 -1
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/profile-vocabulary.d.ts +103 -0
- package/dist/contracts/profile-vocabulary.js +449 -0
- package/dist/contracts/target-dossier.d.ts +21 -21
- package/dist/execution/artifact-registration.js +9 -0
- package/dist/execution/artifact-registry.d.ts +14 -0
- package/dist/execution/input-signature.d.ts +16 -3
- package/dist/execution/input-signature.js +0 -0
- package/dist/execution/post-step-pipeline.d.ts +6 -4
- package/dist/execution/post-step-pipeline.js +18 -9
- package/dist/execution/staged-input.d.ts +4 -4
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -0
- package/dist/input-scan/clustering.d.ts +63 -0
- package/dist/input-scan/clustering.js +208 -0
- package/dist/input-scan/companions.d.ts +30 -0
- package/dist/input-scan/companions.js +138 -0
- package/dist/input-scan/decoder/container_readout.py +171 -0
- package/dist/input-scan/detect-sets.d.ts +24 -0
- package/dist/input-scan/detect-sets.js +279 -0
- package/dist/input-scan/enrich.d.ts +62 -19
- package/dist/input-scan/enrich.js +132 -187
- package/dist/input-scan/formats.d.ts +13 -5
- package/dist/input-scan/formats.js +13 -5
- package/dist/input-scan/index.d.ts +10 -4
- package/dist/input-scan/index.js +7 -4
- package/dist/input-scan/markers.d.ts +21 -0
- package/dist/input-scan/markers.js +89 -0
- package/dist/input-scan/menu.d.ts +127 -0
- package/dist/input-scan/menu.js +0 -0
- package/dist/input-scan/quarantine.d.ts +17 -0
- package/dist/input-scan/quarantine.js +69 -0
- package/dist/input-scan/readout-budget.d.ts +32 -0
- package/dist/input-scan/readout-budget.js +61 -0
- package/dist/input-scan/readout.d.ts +33 -0
- package/dist/input-scan/readout.js +336 -0
- package/dist/input-scan/scan.d.ts +5 -4
- package/dist/input-scan/scan.js +5 -5
- package/dist/input-scan/set-types.d.ts +209 -0
- package/dist/input-scan/set-types.js +15 -0
- package/dist/input-scan/templates.d.ts +93 -0
- package/dist/input-scan/templates.js +369 -0
- package/dist/input-scan/tokens.d.ts +94 -0
- package/dist/input-scan/tokens.js +267 -0
- package/dist/input-scan/tuning.d.ts +55 -0
- package/dist/input-scan/tuning.js +55 -0
- package/dist/input-scan/types.d.ts +2 -2
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +8 -6
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +3 -2
- package/dist/prompts/sandbox/data-profiler.d.ts +10 -1
- package/dist/prompts/sandbox/data-profiler.js +197 -53
- package/dist/prompts/sandbox-standards.d.ts +1 -1
- package/dist/prompts/sandbox-standards.js +10 -7
- package/dist/schemas/data-profile-schemas.d.ts +943 -80
- package/dist/schemas/data-profile-schemas.js +300 -115
- package/dist/state/data-profile.d.ts +1 -1
- package/dist/state/index.d.ts +1 -1
- package/dist/state/vector-store.d.ts +13 -0
- package/dist/state/vector-store.js +8 -0
- package/dist/tasks/data-profile-absorb.d.ts +78 -0
- package/dist/tasks/data-profile-absorb.js +285 -0
- package/dist/tasks/data-profile-index.d.ts +34 -25
- package/dist/tasks/data-profile-index.js +160 -59
- package/dist/tasks/data-profile-resolve.d.ts +72 -0
- package/dist/tasks/data-profile-resolve.js +639 -0
- package/dist/tasks/data-profile.d.ts +16 -5
- package/dist/tasks/data-profile.js +270 -131
- package/dist/tools/research/inspect-data-profile.d.ts +48 -22
- package/dist/tools/research/inspect-data-profile.js +73 -54
- package/dist/tools/workspace/scan-inputs.d.ts +4 -4
- package/dist/tools/workspace/scan-inputs.js +13 -14
- package/dist/tools/workspace/workspace-search.d.ts +11 -2
- package/dist/tools/workspace/workspace-search.js +44 -16
- package/dist/workflows/sandbox-step.js +36 -6
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +21 -21
- package/package.json +5 -3
- package/dist/input-scan/coverage.d.ts +0 -22
- package/dist/input-scan/coverage.js +0 -85
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The controlled vocabulary the profiler draws from, as data.
|
|
3
|
+
*
|
|
4
|
+
* One source, two readers: submit validation derives its enums from these lists and
|
|
5
|
+
* prompt assembly renders from the same entries, so the catalogue the agent is shown
|
|
6
|
+
* and the catalogue the validator enforces cannot diverge. Growth is an edit to these
|
|
7
|
+
* lists, measured by how often `other` is reached for.
|
|
8
|
+
*
|
|
9
|
+
* It lives in `contracts/` because the persisted profile carries these ids: a consumer
|
|
10
|
+
* rendering a stored group needs the category's meaning without importing the ledger,
|
|
11
|
+
* the schema module, or zod. Hosts do not extend it — the package owns the catalogue and
|
|
12
|
+
* versions it with the harness.
|
|
13
|
+
*
|
|
14
|
+
* Every category carries its nearest neighbour and the rule that separates them, because
|
|
15
|
+
* a category list without one is a list of labels an author picks by vibe. Every
|
|
16
|
+
* dimension category additionally carries a default treatment: whether a downstream step
|
|
17
|
+
* would TYPICALLY consume one value's files as a different substrate than another's.
|
|
18
|
+
* The agent follows the default and deviates only with a stated reason.
|
|
19
|
+
*/
|
|
20
|
+
// ── Group roles ────────────────────────────────────────────────────────────
|
|
21
|
+
export const GROUP_ROLE_IDS = ["data", "metadata", "index", "documentation", "reference"];
|
|
22
|
+
/**
|
|
23
|
+
* Companions (`.bai`/`.tbi`/per-file `.md5`) take no role — they attach to members. A
|
|
24
|
+
* checksum or inventory file spanning MANY members is not a companion but a member of
|
|
25
|
+
* its own, category `manifest`; `index` is for tables that exist to be joined against,
|
|
26
|
+
* never for binary sidecars.
|
|
27
|
+
*/
|
|
28
|
+
export const GROUP_ROLES = [
|
|
29
|
+
{ id: "data", definition: "The measurements themselves — variant calls, a count matrix, images." },
|
|
30
|
+
{ id: "metadata", definition: "Describes other members' meaning or provenance — a sample sheet, a pipeline manifest, config files." },
|
|
31
|
+
{ id: "index", definition: "Standalone lookup or join tables — a variable-code dictionary, a file inventory table." },
|
|
32
|
+
{ id: "documentation", definition: "Prose for humans — README, dataset card, methods paper, licences." },
|
|
33
|
+
{
|
|
34
|
+
id: "reference",
|
|
35
|
+
definition: "External or canonical inputs the analysis consumes but did not produce — genome build, annotation GTF, compound library, pathway sets.",
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
// ── Group categories ───────────────────────────────────────────────────────
|
|
39
|
+
export const GROUP_CATEGORY_IDS = [
|
|
40
|
+
"sequencing-reads",
|
|
41
|
+
"alignment",
|
|
42
|
+
"variant-calls",
|
|
43
|
+
"copy-number",
|
|
44
|
+
"structural-variants",
|
|
45
|
+
"association-results",
|
|
46
|
+
"regions-signal",
|
|
47
|
+
"expression-matrix",
|
|
48
|
+
"single-cell-matrix",
|
|
49
|
+
"methylation",
|
|
50
|
+
"proteomics",
|
|
51
|
+
"metabolomics",
|
|
52
|
+
"cytometry",
|
|
53
|
+
"chemical-structures",
|
|
54
|
+
"imaging",
|
|
55
|
+
"clinical-table",
|
|
56
|
+
"sample-annotation",
|
|
57
|
+
"code",
|
|
58
|
+
"document",
|
|
59
|
+
"manifest",
|
|
60
|
+
"reference-data",
|
|
61
|
+
"other",
|
|
62
|
+
];
|
|
63
|
+
/**
|
|
64
|
+
* Coarse on purpose — a free `subtype` refines below the category. Content beats shape:
|
|
65
|
+
* a beta-value matrix is methylation whatever its layout, and a MAF is variant-calls
|
|
66
|
+
* whatever its extension. Grain and meaning decide.
|
|
67
|
+
*/
|
|
68
|
+
export const GROUP_CATEGORIES = [
|
|
69
|
+
{
|
|
70
|
+
id: "sequencing-reads",
|
|
71
|
+
definition: "Reads or raw instrument signal — FASTQ, unaligned BAM, FAST5/POD5.",
|
|
72
|
+
note: "vs alignment: mapped-ness decides — an unaligned BAM is reads.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "alignment",
|
|
76
|
+
definition: "Reads mapped to a reference — BAM/CRAM carrying mapped records.",
|
|
77
|
+
note: "vs sequencing-reads: an unaligned BAM is reads, whatever the container.",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "variant-calls",
|
|
81
|
+
definition: "Per-sample small-variant calls (SNV/indel) — VCF with genotypes, MAF.",
|
|
82
|
+
note: "vs copy-number/structural-variants: those products get their own category whatever the container. vs association-results: per-sample genotypes rather than cohort statistics. vs reference-data: a sites-only population or annotation VCF is reference-data, role reference.",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "copy-number",
|
|
86
|
+
definition: "Copy-number products — segment tables, ratio tracks, CNV calls.",
|
|
87
|
+
note: "vs variant-calls: small variants stay variant-calls.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "structural-variants",
|
|
91
|
+
definition: "Structural-variant products — breakpoints, SV calls.",
|
|
92
|
+
note: "vs variant-calls: an SV VCF is structural-variants.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "association-results",
|
|
96
|
+
definition: "Per-variant or per-feature summary statistics without per-sample data — GWAS/QTL sumstats, PLINK output.",
|
|
97
|
+
note: "vs regions-signal: test statistics rather than positions or coverage.",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "regions-signal",
|
|
101
|
+
definition: "Genomic intervals and coverage tracks — BED/narrowPeak/broadPeak, bigWig/bedGraph.",
|
|
102
|
+
note: "vs association-results: positions and coverage rather than test statistics.",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "expression-matrix",
|
|
106
|
+
definition: "Bulk feature × sample abundance tables.",
|
|
107
|
+
note: "vs single-cell-matrix: barcoded per-cell products are single-cell. vs methylation: content beats shape — a beta-value matrix is methylation.",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "single-cell-matrix",
|
|
111
|
+
definition: "Barcoded per-cell products — MEX triplets, h5ad. Spatial omics is a subtype.",
|
|
112
|
+
note: "vs expression-matrix: bulk gene × sample tables are expression.",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "methylation",
|
|
116
|
+
definition: "Methylation measurements — beta/M-value matrices, per-CpG calls.",
|
|
117
|
+
note: "vs expression-matrix: the measured substrate decides, not the table's shape.",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "proteomics",
|
|
121
|
+
definition: "Protein-level measurements. MS rawness is a subtype.",
|
|
122
|
+
note: "vs imaging: an imaging mass-spec run is proteomics with subtype 'imaging MS' — assay substrate wins.",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: "metabolomics",
|
|
126
|
+
definition: "Metabolite-level measurements. MS rawness is a subtype.",
|
|
127
|
+
note: "vs proteomics: by measured substrate.",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "cytometry",
|
|
131
|
+
definition: "Flow or mass cytometry — FCS and its derivatives.",
|
|
132
|
+
note: "vs imaging: per-event measurements rather than pixels; vs proteomics: neither.",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "chemical-structures",
|
|
136
|
+
definition: "Compound structures and libraries — SDF, SMILES, MOL.",
|
|
137
|
+
note: "vs reference-data: a screening library the analysis measures against is reference-data, role reference.",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: "imaging",
|
|
141
|
+
definition: "Microscopy, radiology, and other pixel data — DICOM, TIFF stacks.",
|
|
142
|
+
note: "vs cytometry: pixels rather than per-event measurements.",
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "clinical-table",
|
|
146
|
+
definition: "Facts about subjects over time — diagnoses, medications, outcomes, visits.",
|
|
147
|
+
note: "vs sample-annotation: subject grain rather than specimen grain. When one table does both, prefer sample-annotation only if its grain is the sample.",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "sample-annotation",
|
|
151
|
+
definition: "Specimen or measurement attribute maps — sample sheets, characteristic tables.",
|
|
152
|
+
note: "vs clinical-table: specimen attributes rather than subject history.",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "code",
|
|
156
|
+
definition: "Scripts, notebooks, and workflow definitions arriving as inputs.",
|
|
157
|
+
note: "vs document: executable rather than prose — a notebook is code even when narrated.",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: "document",
|
|
161
|
+
definition: "Prose for humans — README, dataset card, methods paper, licence.",
|
|
162
|
+
note: "vs manifest: a dataset card in Markdown is a document; its JSON twin is a manifest.",
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "manifest",
|
|
166
|
+
definition: "Machine-readable inventories of these files — checksum lists, file manifests.",
|
|
167
|
+
note: "vs index role: an inventory OF these files is a manifest; a table that exists to be joined against data is index-role.",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "reference-data",
|
|
171
|
+
definition: "Consumed-not-produced canonical inputs — genome builds, GTFs, pathway sets, spectral libraries, sites-only VCFs.",
|
|
172
|
+
note: "vs everything: the analysis consumes it and did not produce it.",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "other",
|
|
176
|
+
definition: "Outside the catalogue — carry a free label saying what it is.",
|
|
177
|
+
note: "A wrong specific category is worse than an honest `other` with a good label. Electrophysiology, phylogenetic trees, model weights, database dumps, and plate-reader tables belong here today.",
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
// ── Dimension categories ───────────────────────────────────────────────────
|
|
181
|
+
export const DIMENSION_CATEGORY_IDS = [
|
|
182
|
+
"subject",
|
|
183
|
+
"sample",
|
|
184
|
+
"organism-species",
|
|
185
|
+
"model-system",
|
|
186
|
+
"cohort-arm",
|
|
187
|
+
"disease-state",
|
|
188
|
+
"treatment",
|
|
189
|
+
"timepoint",
|
|
190
|
+
"tissue-site",
|
|
191
|
+
"cell-type",
|
|
192
|
+
"genetic-perturbation",
|
|
193
|
+
"sex",
|
|
194
|
+
"age-group",
|
|
195
|
+
"developmental-stage",
|
|
196
|
+
"environment",
|
|
197
|
+
"assay-modality",
|
|
198
|
+
"assay-target",
|
|
199
|
+
"variant-origin",
|
|
200
|
+
"sample-pairing",
|
|
201
|
+
"processing-level",
|
|
202
|
+
"assay-version",
|
|
203
|
+
"batch",
|
|
204
|
+
"replicate",
|
|
205
|
+
"tool-variant",
|
|
206
|
+
"library-prep",
|
|
207
|
+
"data-partition",
|
|
208
|
+
"other",
|
|
209
|
+
];
|
|
210
|
+
export const DIMENSION_CATEGORIES = [
|
|
211
|
+
{
|
|
212
|
+
id: "subject",
|
|
213
|
+
scope: "biological",
|
|
214
|
+
definition: "The individual the data is about — patient, animal, donor. For a xenograft the subject is the donor; the host is genotype context.",
|
|
215
|
+
defaultTreatment: "dimension",
|
|
216
|
+
note: "Never split. Not the specimen — see sample.",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "sample",
|
|
220
|
+
scope: "biological",
|
|
221
|
+
definition: "Physical specimen. Nests under subject where one exists; community and environmental samples have none.",
|
|
222
|
+
defaultTreatment: "dimension",
|
|
223
|
+
note: "Never split. Not the library or aliquot, which is technical — see library-prep.",
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: "organism-species",
|
|
227
|
+
scope: "biological",
|
|
228
|
+
definition: "Species, when it varies across the dataset.",
|
|
229
|
+
defaultTreatment: "split",
|
|
230
|
+
note: "Species picks the reference genome. A constant organism is an identity field, not a dimension.",
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: "model-system",
|
|
234
|
+
scope: "biological",
|
|
235
|
+
definition: "Cell line, organoid, or model identity in subject-less panels.",
|
|
236
|
+
defaultTreatment: "dimension",
|
|
237
|
+
note: "A line used as the subject surrogate is still model-system, not subject.",
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: "cohort-arm",
|
|
241
|
+
scope: "biological",
|
|
242
|
+
definition: "Grouping assigned by design — randomisation, dose groups, discovery/validation cohorts. Assigned-by-design is the sole test.",
|
|
243
|
+
defaultTreatment: "dimension",
|
|
244
|
+
note: "Comparative models consume all arms together. Split only for independent cohorts, as a stated deviation. Observed conditions are disease-state.",
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "disease-state",
|
|
248
|
+
scope: "biological",
|
|
249
|
+
definition: "Observed diagnosis or condition varying across subjects, including observational case/control.",
|
|
250
|
+
defaultTreatment: "dimension",
|
|
251
|
+
note: "Assigned-by-design grouping is cohort-arm.",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: "treatment",
|
|
255
|
+
scope: "biological",
|
|
256
|
+
definition: "Compound, dose, or intervention as the varying quantity, including protocol-administered diet.",
|
|
257
|
+
defaultTreatment: "dimension",
|
|
258
|
+
note: "Dose-response curves are consumed together. Named designed groups are cohort-arm; ambient exposure is environment.",
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
id: "timepoint",
|
|
262
|
+
scope: "biological",
|
|
263
|
+
definition: "When the measurement was taken, in subject or visit time.",
|
|
264
|
+
defaultTreatment: "dimension",
|
|
265
|
+
note: "Known-ambiguous and documented as such. Acquisition, run, and processing dates are batch; duration of exposure is treatment.",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
id: "tissue-site",
|
|
269
|
+
scope: "biological",
|
|
270
|
+
definition: "Organism part, sampling site, biofluid, or subcellular fraction, when it varies.",
|
|
271
|
+
defaultTreatment: "dimension",
|
|
272
|
+
note: "Ambiguous like timepoint. A constant tissue is an identity field.",
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: "cell-type",
|
|
276
|
+
scope: "biological",
|
|
277
|
+
definition: "Sorted population or annotated type, when it varies across files.",
|
|
278
|
+
defaultTreatment: "dimension",
|
|
279
|
+
note: "Per-cell annotations inside one matrix are file content, not a dimension.",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: "genetic-perturbation",
|
|
283
|
+
scope: "biological",
|
|
284
|
+
definition: "Strain, genetic modification, KO/WT, CRISPR guide, shRNA, or ORF per member.",
|
|
285
|
+
defaultTreatment: "dimension",
|
|
286
|
+
note: "Perturbation as the study's designed comparison is cohort-arm.",
|
|
287
|
+
},
|
|
288
|
+
{ id: "sex", scope: "biological", definition: "Sex as a varying attribute.", defaultTreatment: "dimension", note: "A constant is an identity field." },
|
|
289
|
+
{
|
|
290
|
+
id: "age-group",
|
|
291
|
+
scope: "biological",
|
|
292
|
+
definition: "Age or age band as a varying attribute.",
|
|
293
|
+
defaultTreatment: "dimension",
|
|
294
|
+
note: "Calendar age; biology is developmental-stage.",
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
id: "developmental-stage",
|
|
298
|
+
scope: "biological",
|
|
299
|
+
definition: "Developmental stage as a varying attribute.",
|
|
300
|
+
defaultTreatment: "dimension",
|
|
301
|
+
note: "Age-group is calendar; stage is biology.",
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: "environment",
|
|
305
|
+
scope: "biological",
|
|
306
|
+
definition: "Diet, stimulus, growth condition, field location.",
|
|
307
|
+
defaultTreatment: "dimension",
|
|
308
|
+
note: "Administered per protocol is treatment; plot or block structure is batch.",
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "assay-modality",
|
|
312
|
+
scope: "technical",
|
|
313
|
+
definition: "Which measurement technology — RNA-seq vs proteomics vs imaging.",
|
|
314
|
+
defaultTreatment: "split",
|
|
315
|
+
note: "Different substrates. Different-marker panels are a different modality or target, not a version; a version of one assay is assay-version.",
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "assay-target",
|
|
319
|
+
scope: "technical",
|
|
320
|
+
definition: "The molecular target an assay is directed at — a ChIP antibody target, a panel's target set.",
|
|
321
|
+
defaultTreatment: "split",
|
|
322
|
+
note: "Each target's output is a distinct substrate. Modality is the technology; target is what it points at.",
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "variant-origin",
|
|
326
|
+
scope: "technical",
|
|
327
|
+
definition: "Somatic vs germline.",
|
|
328
|
+
defaultTreatment: "split",
|
|
329
|
+
note: "The canonical substrate case.",
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: "sample-pairing",
|
|
333
|
+
scope: "technical",
|
|
334
|
+
definition: "Tumour/normal or matched-pair membership.",
|
|
335
|
+
defaultTreatment: "split",
|
|
336
|
+
note: "Split with the pair id kept on both halves, because paired callers consume both together. Pairing is within-subject; cohort-arm is between-subjects.",
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "processing-level",
|
|
340
|
+
scope: "technical",
|
|
341
|
+
definition: "Raw, filtered, and normalised tiers of the same product.",
|
|
342
|
+
defaultTreatment: "split",
|
|
343
|
+
note: "Steps consume one tier. Alternate tools at the same tier are tool-variant.",
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
id: "assay-version",
|
|
347
|
+
scope: "technical",
|
|
348
|
+
definition: "Panel, kit, or pipeline version over the same target set.",
|
|
349
|
+
defaultTreatment: "dimension",
|
|
350
|
+
note: "Batch-like. Different marker sets are assay-modality or assay-target.",
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
id: "batch",
|
|
354
|
+
scope: "technical",
|
|
355
|
+
definition: "Plate, lane, centre, run or acquisition date.",
|
|
356
|
+
defaultTreatment: "dimension",
|
|
357
|
+
note: "Never split. Subject-visit time is timepoint.",
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
id: "replicate",
|
|
361
|
+
scope: "technical",
|
|
362
|
+
definition: "Replicate index. The label must state whether the replication is biological or technical.",
|
|
363
|
+
defaultTreatment: "dimension",
|
|
364
|
+
note: "Never split.",
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
id: "tool-variant",
|
|
368
|
+
scope: "technical",
|
|
369
|
+
definition: "Alternate callers or aligners over the same input.",
|
|
370
|
+
defaultTreatment: "dimension",
|
|
371
|
+
note: "Consumed together for consensus. Pipeline versions over time are assay-version.",
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "library-prep",
|
|
375
|
+
scope: "technical",
|
|
376
|
+
definition: "Library, label, capture, or aliquot.",
|
|
377
|
+
defaultTreatment: "dimension",
|
|
378
|
+
note: "A biofluid or subcellular fraction is biological — tissue-site.",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
id: "data-partition",
|
|
382
|
+
scope: "technical",
|
|
383
|
+
definition: "Chromosome shard, file chunk, read-pair half, paged export.",
|
|
384
|
+
defaultTreatment: "dimension",
|
|
385
|
+
note: "Never split, rarely promoted — pure mechanics that stays on the set.",
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: "other",
|
|
389
|
+
scope: "technical",
|
|
390
|
+
definition: "Outside the catalogue — carry a free label saying what varies.",
|
|
391
|
+
defaultTreatment: "dimension",
|
|
392
|
+
note: "Monitored: a wrong specific category is worse than an honest `other`.",
|
|
393
|
+
},
|
|
394
|
+
];
|
|
395
|
+
// ── Probe list ─────────────────────────────────────────────────────────────
|
|
396
|
+
export const DIMENSION_PROBE_IDS = ["subject", "sample", "cohort-arm", "timepoint", "batch"];
|
|
397
|
+
/**
|
|
398
|
+
* A probe outcome, exactly one per probe.
|
|
399
|
+
*
|
|
400
|
+
* `not-found` is a correct and complete answer once the searched set is named — that is
|
|
401
|
+
* what removes the completeness pressure that otherwise invents dimensions. `attested`
|
|
402
|
+
* records prose-only evidence and can never justify a split.
|
|
403
|
+
*/
|
|
404
|
+
export const PROBE_OUTCOME_IDS = ["found", "not-found", "found-but-constant", "attested"];
|
|
405
|
+
/**
|
|
406
|
+
* The only dimensions the agent actively looks for. The searchable set is bounded and
|
|
407
|
+
* named: files in metadata- or documentation-role groups plus clinical-table and
|
|
408
|
+
* sample-annotation members, up to about ten files. Everything off this list is recorded
|
|
409
|
+
* only when encountered during ordinary orientation reading.
|
|
410
|
+
*
|
|
411
|
+
* Assay-modality and replicate are deliberately absent: varying modality is already
|
|
412
|
+
* surfaced by group categories, and probing for replicates invites inferring replicate
|
|
413
|
+
* structure from sample counts, which is fabrication by another name.
|
|
414
|
+
*/
|
|
415
|
+
export const DIMENSION_PROBES = [
|
|
416
|
+
{
|
|
417
|
+
id: "subject",
|
|
418
|
+
guidance: "Identifier slots in per-subject trees; subject-ID columns in clinical or sample tables. A reference-only tree is a legitimate not-found — a genome build's species is not a subject.",
|
|
419
|
+
},
|
|
420
|
+
{ id: "sample", guidance: "Sample-ID slots or columns; sample sheets. Nests under subject when both exist." },
|
|
421
|
+
{
|
|
422
|
+
id: "cohort-arm",
|
|
423
|
+
guidance: "Arm or group columns whose values are assigned by design. An observed condition or diagnosis column is disease-state — answer not-found and point to where it landed. Observational data typically has no arms.",
|
|
424
|
+
},
|
|
425
|
+
{ id: "timepoint", guidance: "Visit, day, or cycle columns; timepoint tokens in names. Acquisition and run dates are batch." },
|
|
426
|
+
{ id: "batch", guidance: "Plate, lane, or run columns; run-date tokens; centre identifiers. Subject-visit time is timepoint." },
|
|
427
|
+
];
|
|
428
|
+
// ── Lookups ────────────────────────────────────────────────────────────────
|
|
429
|
+
const GROUP_CATEGORY_BY_ID = new Map(GROUP_CATEGORIES.map((entry) => [entry.id, entry]));
|
|
430
|
+
const DIMENSION_CATEGORY_BY_ID = new Map(DIMENSION_CATEGORIES.map((entry) => [entry.id, entry]));
|
|
431
|
+
export function groupCategoryEntry(id) {
|
|
432
|
+
return GROUP_CATEGORY_BY_ID.get(id);
|
|
433
|
+
}
|
|
434
|
+
export function dimensionCategoryEntry(id) {
|
|
435
|
+
return DIMENSION_CATEGORY_BY_ID.get(id);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* The scope a dimension takes.
|
|
439
|
+
*
|
|
440
|
+
* Derived from the category for everything in the catalogue. `other` is by definition
|
|
441
|
+
* outside it, so there is nothing to derive from and the scope is the agent's to declare:
|
|
442
|
+
* a biological thing that varies and has no category is still biological, and pinning
|
|
443
|
+
* every `other` to technical would misfile it. Undeclared, it defaults to technical.
|
|
444
|
+
*/
|
|
445
|
+
export function dimensionScope(id, declared) {
|
|
446
|
+
if (id === "other")
|
|
447
|
+
return declared ?? "technical";
|
|
448
|
+
return DIMENSION_CATEGORY_BY_ID.get(id)?.scope ?? "technical";
|
|
449
|
+
}
|
|
@@ -9342,6 +9342,27 @@ export declare const DossierBodySchema: z.ZodObject<{
|
|
|
9342
9342
|
coverage: z.ZodLiteral<"not_loaded">;
|
|
9343
9343
|
reason: z.ZodOptional<z.ZodString>;
|
|
9344
9344
|
}, z.core.$strip>], "coverage">;
|
|
9345
|
+
entity: z.ZodObject<{
|
|
9346
|
+
canonicalId: z.ZodString;
|
|
9347
|
+
symbol: z.ZodString;
|
|
9348
|
+
displayName: z.ZodString;
|
|
9349
|
+
entityType: z.ZodEnum<{
|
|
9350
|
+
gene: "gene";
|
|
9351
|
+
rna: "rna";
|
|
9352
|
+
protein: "protein";
|
|
9353
|
+
complex: "complex";
|
|
9354
|
+
}>;
|
|
9355
|
+
ontology: z.ZodString;
|
|
9356
|
+
identifiers: z.ZodObject<{
|
|
9357
|
+
hgnc: z.ZodOptional<z.ZodString>;
|
|
9358
|
+
ensembl: z.ZodOptional<z.ZodString>;
|
|
9359
|
+
uniprot: z.ZodOptional<z.ZodString>;
|
|
9360
|
+
chembl: z.ZodOptional<z.ZodString>;
|
|
9361
|
+
ncbiGene: z.ZodOptional<z.ZodString>;
|
|
9362
|
+
}, z.core.$strip>;
|
|
9363
|
+
synonyms: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
9364
|
+
proteinFamily: z.ZodOptional<z.ZodString>;
|
|
9365
|
+
}, z.core.$strip>;
|
|
9345
9366
|
off_target_panel: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9346
9367
|
coverage: z.ZodLiteral<"available">;
|
|
9347
9368
|
data: z.ZodObject<{
|
|
@@ -9565,27 +9586,6 @@ export declare const DossierBodySchema: z.ZodObject<{
|
|
|
9565
9586
|
inferred_therapeutic_area: z.ZodNullable<z.ZodString>;
|
|
9566
9587
|
no_liabilities_disclosure: z.ZodOptional<z.ZodString>;
|
|
9567
9588
|
}, z.core.$strict>;
|
|
9568
|
-
entity: z.ZodObject<{
|
|
9569
|
-
canonicalId: z.ZodString;
|
|
9570
|
-
symbol: z.ZodString;
|
|
9571
|
-
displayName: z.ZodString;
|
|
9572
|
-
entityType: z.ZodEnum<{
|
|
9573
|
-
gene: "gene";
|
|
9574
|
-
rna: "rna";
|
|
9575
|
-
protein: "protein";
|
|
9576
|
-
complex: "complex";
|
|
9577
|
-
}>;
|
|
9578
|
-
ontology: z.ZodString;
|
|
9579
|
-
identifiers: z.ZodObject<{
|
|
9580
|
-
hgnc: z.ZodOptional<z.ZodString>;
|
|
9581
|
-
ensembl: z.ZodOptional<z.ZodString>;
|
|
9582
|
-
uniprot: z.ZodOptional<z.ZodString>;
|
|
9583
|
-
chembl: z.ZodOptional<z.ZodString>;
|
|
9584
|
-
ncbiGene: z.ZodOptional<z.ZodString>;
|
|
9585
|
-
}, z.core.$strip>;
|
|
9586
|
-
synonyms: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
9587
|
-
proteinFamily: z.ZodOptional<z.ZodString>;
|
|
9588
|
-
}, z.core.$strip>;
|
|
9589
9589
|
indications: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9590
9590
|
coverage: z.ZodLiteral<"available">;
|
|
9591
9591
|
data: z.ZodObject<{
|
|
@@ -49,6 +49,15 @@ logger = createNoopLogger()) {
|
|
|
49
49
|
await upsertArtifacts(db, localEntries);
|
|
50
50
|
const localPaths = new Set(localEntries.map((e) => e.path));
|
|
51
51
|
const result = await registry.register(input, session);
|
|
52
|
+
// The registry excluded these from `failed` by its own severity judgement, so
|
|
53
|
+
// this is their only record. Logged here rather than left to the registry:
|
|
54
|
+
// the seam is implementable by an embedder, and a rejection does not get to
|
|
55
|
+
// go unrecorded because one implementation chose not to log it.
|
|
56
|
+
if (result.notCounted && result.notCounted.length > 0) {
|
|
57
|
+
log.warn("registry rejections excluded from the failure count", {
|
|
58
|
+
rejected: result.notCounted.map((f) => ({ path: f.path, error: f.error })),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
52
61
|
let externalRegistered = 0;
|
|
53
62
|
for (const reg of result.registered) {
|
|
54
63
|
// Skip files the registry also returned that we didn't upsert locally
|
|
@@ -64,6 +64,20 @@ export interface ExternalRegistrationResult {
|
|
|
64
64
|
* back a whole batch (`failed` then carries one summary entry).
|
|
65
65
|
*/
|
|
66
66
|
failedCount: number;
|
|
67
|
+
/**
|
|
68
|
+
* Rejections the registry judged non-terminal and deliberately kept out of
|
|
69
|
+
* `failed`/`failedCount`: rows it never attempted, so nothing registered and
|
|
70
|
+
* no bytes are at risk. Reported rather than dropped — excluding a rejection
|
|
71
|
+
* decides only that it is not worth failing a step over, never that it goes
|
|
72
|
+
* unrecorded. Keeping them out of `failed` is what lets the fail-fast message
|
|
73
|
+
* list only the paths that actually cost the step something; surfacing them
|
|
74
|
+
* here is what keeps the registry's verdict checkable against what the
|
|
75
|
+
* external system actually said.
|
|
76
|
+
*/
|
|
77
|
+
notCounted?: ReadonlyArray<{
|
|
78
|
+
path: string;
|
|
79
|
+
error: string;
|
|
80
|
+
}>;
|
|
67
81
|
}
|
|
68
82
|
export interface ArtifactRegistry {
|
|
69
83
|
/**
|
|
@@ -2,14 +2,21 @@
|
|
|
2
2
|
* The input signature — the fixed-width comparand a completed profile persists in
|
|
3
3
|
* place of a per-file identity list.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* several thousand identifiers
|
|
5
|
+
* A per-file identity list would have exactly one consumer, the staleness comparison —
|
|
6
|
+
* several thousand identifiers persisted, detoasted on every read of the row, and
|
|
7
7
|
* compared element-wise to answer a question 40 bytes answer. The signature covers
|
|
8
8
|
* `size` and `mtimeMs` as well, so it also notices that the bytes behind a path
|
|
9
9
|
* changed — a `fileId` is a path identity and survives an in-place edit unchanged.
|
|
10
10
|
*
|
|
11
11
|
* Order independence is a property of the digest, not of the caller: entries are
|
|
12
12
|
* sorted canonically before hashing, so two enumerations of the same set agree.
|
|
13
|
+
*
|
|
14
|
+
* It digests KEPT files only — quarantine runs before the digest, on both sides of the
|
|
15
|
+
* comparison. A partial-download twin or an OS droppings file was never part of what was
|
|
16
|
+
* profiled, so its appearance, edit, or removal is not drift, and a profile that
|
|
17
|
+
* re-triggered on it would re-run on churn nobody caused. A change to the quarantine rules
|
|
18
|
+
* themselves recomputes every signature once on the next scan, which is the correct
|
|
19
|
+
* consequence: the definition of "kept" changed.
|
|
13
20
|
*/
|
|
14
21
|
import type { DataProfileInputSignature } from "../state/data-profile.js";
|
|
15
22
|
import type { StagedInput } from "./staged-input.js";
|
|
@@ -18,9 +25,15 @@ export interface SignableInput {
|
|
|
18
25
|
readonly fileId: string;
|
|
19
26
|
readonly size: number;
|
|
20
27
|
readonly mtimeMs?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Path relative to the data dir — what quarantine reads. An entry carrying none is
|
|
30
|
+
* always kept: a caller that cannot name a path cannot be asked to prove its file is
|
|
31
|
+
* not junk, and dropping it would be the one outcome worse than including it.
|
|
32
|
+
*/
|
|
33
|
+
readonly relativePath?: string;
|
|
21
34
|
}
|
|
22
35
|
/**
|
|
23
|
-
* Digest a staged input set.
|
|
36
|
+
* Digest a staged input set, quarantine first.
|
|
24
37
|
*
|
|
25
38
|
* A workflow input persisted before `StagedInput.mtimeMs` existed deserializes without
|
|
26
39
|
* it despite the compile-time type; such an entry contributes `-` in the mtime field
|
|
Binary file
|
|
@@ -56,10 +56,12 @@ export declare function generateStepSummaryAndWrite(deps: PostStepPipelineDeps,
|
|
|
56
56
|
/**
|
|
57
57
|
* Reconcile the manifest against disk (drop phantoms, rehash) and register the
|
|
58
58
|
* survivors with the local ledger + the injected `ArtifactRegistry`. Fail-fast
|
|
59
|
-
* (see the artifact-manifest spec):
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* the
|
|
59
|
+
* (see the artifact-manifest spec): `externalFailed` counts only the terminal
|
|
60
|
+
* rejections — a rejected output whose bytes exist nowhere but the step tree,
|
|
61
|
+
* plus anything rejected as a consequence of one — so a non-zero count means
|
|
62
|
+
* real outputs went unregistered, and it throws with the per-file detail (the
|
|
63
|
+
* OSS filesystem registry returns `externalFailed: 0`, so it never trips).
|
|
64
|
+
* Returns the reconciled manifest.
|
|
63
65
|
*/
|
|
64
66
|
export declare function reconcileAndRegisterStepArtifacts(deps: PostStepPipelineDeps, postCtx: PostStepContext, manifest: readonly ArtifactManifestEntry[]): Promise<readonly ArtifactManifestEntry[]>;
|
|
65
67
|
/**
|
|
@@ -95,10 +95,12 @@ export async function generateStepSummaryAndWrite(deps, postCtx, manifest) {
|
|
|
95
95
|
/**
|
|
96
96
|
* Reconcile the manifest against disk (drop phantoms, rehash) and register the
|
|
97
97
|
* survivors with the local ledger + the injected `ArtifactRegistry`. Fail-fast
|
|
98
|
-
* (see the artifact-manifest spec):
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* the
|
|
98
|
+
* (see the artifact-manifest spec): `externalFailed` counts only the terminal
|
|
99
|
+
* rejections — a rejected output whose bytes exist nowhere but the step tree,
|
|
100
|
+
* plus anything rejected as a consequence of one — so a non-zero count means
|
|
101
|
+
* real outputs went unregistered, and it throws with the per-file detail (the
|
|
102
|
+
* OSS filesystem registry returns `externalFailed: 0`, so it never trips).
|
|
103
|
+
* Returns the reconciled manifest.
|
|
102
104
|
*/
|
|
103
105
|
export async function reconcileAndRegisterStepArtifacts(deps, postCtx, manifest) {
|
|
104
106
|
const { input, session, lineageCollector } = postCtx;
|
|
@@ -122,11 +124,18 @@ export async function reconcileAndRegisterStepArtifacts(deps, postCtx, manifest)
|
|
|
122
124
|
collector: lineageCollector,
|
|
123
125
|
}, session, deps.logger);
|
|
124
126
|
if (reg.externalFailed > 0) {
|
|
125
|
-
|
|
127
|
+
// The registry commits per leaf and per activity, so accepted and rejected
|
|
128
|
+
// rows arriving together is ordinary and `externalRegistered` is what
|
|
129
|
+
// genuinely landed. Zero of it means this batch registered nothing at all:
|
|
130
|
+
// no row carries an `artifact_id`, so the byte-sync that follows selects
|
|
131
|
+
// none of them and every output stays local-only. That reads as a payload-
|
|
132
|
+
// or credential-shaped rejection rather than a per-file one, so it rides on
|
|
133
|
+
// the message and as its own field.
|
|
134
|
+
const noneRegistered = reg.externalRegistered === 0;
|
|
126
135
|
const detail = reg.failureDetails.map((f) => `${f.path}: ${f.error}`).join("\n ");
|
|
127
136
|
const msg = `[post-step.reconcile] external registration failed for ${input.stepId}: ` +
|
|
128
|
-
`${reg.externalFailed}
|
|
129
|
-
(
|
|
137
|
+
`${reg.externalFailed} terminal rejection(s), ${reg.externalRegistered}/${reg.localCount} local artifact(s) registered` +
|
|
138
|
+
(noneRegistered ? " (nothing in this batch registered)" : "") +
|
|
130
139
|
(detail ? `\n ${detail}` : "");
|
|
131
140
|
// Logged as fields as well as thrown: the throw reaches `failStep` as one
|
|
132
141
|
// opaque string, so the per-path rejections are only queryable from here.
|
|
@@ -138,7 +147,7 @@ export async function reconcileAndRegisterStepArtifacts(deps, postCtx, manifest)
|
|
|
138
147
|
externalFailed: reg.externalFailed,
|
|
139
148
|
externalRegistered: reg.externalRegistered,
|
|
140
149
|
localCount: reg.localCount,
|
|
141
|
-
|
|
150
|
+
noneRegistered,
|
|
142
151
|
failures: reg.failureDetails,
|
|
143
152
|
});
|
|
144
153
|
throw new Error(msg);
|
|
@@ -203,7 +212,7 @@ export async function vectorIndexStepOutputs(deps, postCtx, artifacts) {
|
|
|
203
212
|
let indexed = 0;
|
|
204
213
|
let failed = 0;
|
|
205
214
|
for (const entry of liveMetadata) {
|
|
206
|
-
const ok = await indexOne(`/${input.analysisId}/${entry.dbPath}`, entry.description, { text: entry.description, type: "output", ...entry.metadata });
|
|
215
|
+
const ok = await indexOne(`/${input.analysisId}/${entry.dbPath}`, entry.description, { text: entry.description, type: "output", ...entry.metadata, path: entry.dbPath });
|
|
207
216
|
if (ok)
|
|
208
217
|
indexed++;
|
|
209
218
|
else
|