Skip to content

Commit 50d8fbf

Browse files
committed
Hard-cut fulfillment contract rename across Bloom surfaces
1 parent d4f8011 commit 50d8fbf

23 files changed

+896
-210
lines changed

ATLAS_BLOOM_API_GUIDANCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Beta contract guardrails:
2323

2424
1. The atomic business/fulfillment/reporting unit is `TRF.test`; `TRF` is rollup context only.
2525
2. External contracts are EUID-only (do not depend on UUIDs).
26-
3. Container context may link to multiple tests on the same TRF; direct container-linked tests/process-items are the authoritative processing set.
26+
3. Container context may link to multiple tests on the same TRF; direct container-linked tests/fulfillment-items are the authoritative processing set.
2727

2828
Beta work-control endpoints (queue claim/reservation/consume):
2929

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Canonical queues:
4646
- `ilmn_start_seq_run`
4747
- `ont_start_seq_run`
4848

49-
Atlas records intake outcomes first. Bloom accepts only Atlas-approved material, links that material to Atlas TRF/Test/process-item context through explicit graph-linked reference objects, and preserves lineage from specimen/container through plate and well placement, library prep, pooling, sequencing run creation, and sequenced library assignment.
49+
Atlas records intake outcomes first. Bloom accepts only Atlas-approved material, links that material to Atlas TRF/Test/fulfillment-item context through explicit graph-linked reference objects, and preserves lineage from specimen/container through plate and well placement, library prep, pooling, sequencing run creation, and sequenced library assignment.
5050
The atomic business/fulfillment/reporting unit is `TRF.test`; `TRF` is a rollup container across child tests.
5151
Accepted-material ingress queue membership is applied to the physical container (`container_euid`), with specimen queue reads falling back to containing-container queue state when needed.
5252

@@ -63,7 +63,7 @@ Bloom returns:
6363
- `atlas_tenant_id`
6464
- `atlas_trf_euid`
6565
- `atlas_test_euid`
66-
- `atlas_test_process_item_euid`
66+
- `atlas_test_fulfillment_item_euid`
6767

6868
Public beta APIs return EUIDs only. Internal UUIDs are not part of the supported contract.
6969

@@ -137,7 +137,7 @@ Workflow/workset runtime surfaces are retired from active API/GUI mounts for the
137137

138138
## Development
139139

140-
Bloom runs on the TapDB-backed adapter layer used across the LSMC refactor. The beta queue flow uses explicit object creation, lineage writes, targeted lookup queries, process-item references, and idempotency keys on direct integration calls.
140+
Bloom runs on the TapDB-backed adapter layer used across the LSMC refactor. The beta queue flow uses explicit object creation, lineage writes, targeted lookup queries, fulfillment-item references, and idempotency keys on direct integration calls.
141141

142142
Focused validation commands for the beta path:
143143

bloom_lims/domain/beta_actions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
_ACTION_TEMPLATE_DEFINITIONS: dict[str, dict[str, Any]] = {
2222
"register_accepted_material": {
2323
"name": "Register Accepted Material",
24-
"description": "Creates accepted Bloom beta material and links Atlas process-item references.",
24+
"description": "Creates accepted Bloom beta material and links Atlas fulfillment-item references.",
2525
},
2626
"create_empty_tube": {
2727
"name": "Create Empty Tube",

bloom_lims/domain/beta_lab.py

Lines changed: 69 additions & 69 deletions
Large diffs are not rendered by default.

bloom_lims/gui/routes/modern.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,20 @@ def _render_search_page(
6464
async def modern_dashboard(request: Request, _=Depends(require_auth)):
6565
user_data = request.session.get("user_data", {})
6666
stats = {
67-
"assays_total": 0,
67+
"queue_runtime_total": 0,
6868
"objects_total": 0,
6969
"equipment_total": 0,
7070
"reagents_total": 0,
7171
}
72-
recent_assays = []
72+
recent_queue_runtime = []
7373
recent_objects = []
7474
db_unavailable = not _is_tapdb_reachable()
7575

7676
if not db_unavailable:
7777
try:
7878
bobdb = BloomObj(BLOOMdb3(app_username=user_data.get("email", "anonymous")))
7979
stats = {
80-
"assays_total": bobdb.session.query(bobdb.Base.classes.workflow_instance)
80+
"queue_runtime_total": bobdb.session.query(bobdb.Base.classes.workflow_instance)
8181
.filter_by(is_deleted=False, is_singleton=True)
8282
.count(),
8383
"objects_total": bobdb.session.query(bobdb.Base.classes.generic_instance)
@@ -93,7 +93,7 @@ async def modern_dashboard(request: Request, _=Depends(require_auth)):
9393
)
9494
.count(),
9595
}
96-
recent_assays = (
96+
recent_queue_runtime = (
9797
bobdb.session.query(bobdb.Base.classes.workflow_instance)
9898
.filter_by(is_deleted=False, is_singleton=True)
9999
.order_by(bobdb.Base.classes.workflow_instance.created_dt.desc())
@@ -115,7 +115,7 @@ async def modern_dashboard(request: Request, _=Depends(require_auth)):
115115
"request": request,
116116
"udat": user_data,
117117
"stats": stats,
118-
"recent_assays": recent_assays,
118+
"recent_queue_runtime": recent_queue_runtime,
119119
"recent_objects": recent_objects,
120120
"db_unavailable": db_unavailable,
121121
}

bloom_lims/gui/routes/operations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,9 @@ async def assays(request: Request, show_type: str = "all", _auth=Depends(require
275275
atype = {}
276276

277277
if show_type == "assay":
278-
atype["type"] = "Assays"
278+
atype["type"] = "Queue Runtime"
279279
else:
280-
atype["type"] = "All Assays, etc"
280+
atype["type"] = "All Queue Runtime Objects"
281281

282282
for i in sorted(ay_ds.keys()):
283283
assays.append(ay_ds[i])

bloom_lims/schemas/beta_lab.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
]
2020

2121

22-
class AtlasProcessItemReference(BaseModel):
22+
class AtlasFulfillmentItemReference(BaseModel):
2323
atlas_test_euid: str
24-
atlas_test_process_item_euid: str
24+
atlas_test_fulfillment_item_euid: str
2525

2626

2727
class AtlasCollectionEventSnapshot(BaseModel):
@@ -36,7 +36,7 @@ class AtlasCollectionEventSnapshot(BaseModel):
3636
expected_label_text: str | None = None
3737

3838

39-
class AtlasProcessContext(BaseModel):
39+
class AtlasFulfillmentContext(BaseModel):
4040
atlas_tenant_id: str
4141
atlas_trf_euid: str | None = None
4242
atlas_test_euid: str | None = None
@@ -47,10 +47,10 @@ class AtlasProcessContext(BaseModel):
4747
atlas_organization_site_euid: str | None = None
4848
atlas_collection_event_euid: str | None = None
4949
collection_event_snapshot: AtlasCollectionEventSnapshot | None = None
50-
process_items: list[AtlasProcessItemReference] = Field(default_factory=list)
50+
fulfillment_items: list[AtlasFulfillmentItemReference] = Field(default_factory=list)
5151

5252
@model_validator(mode="after")
53-
def validate_context(self) -> "AtlasProcessContext":
53+
def validate_context(self) -> "AtlasFulfillmentContext":
5454
if not self.atlas_tenant_id.strip():
5555
raise ValueError("atlas_tenant_id is required")
5656
if self.atlas_trf_euid is not None and not self.atlas_trf_euid.strip():
@@ -100,9 +100,9 @@ def validate_context(self) -> "AtlasProcessContext":
100100
"collection_event_snapshot.collection_event_euid must match "
101101
"atlas_collection_event_euid"
102102
)
103-
if self.process_items:
103+
if self.fulfillment_items:
104104
if not (self.atlas_trf_euid and self.atlas_trf_euid.strip()):
105-
raise ValueError("atlas_trf_euid is required when process_items are provided")
105+
raise ValueError("atlas_trf_euid is required when fulfillment_items are provided")
106106
elif (self.atlas_test_euid is not None or self.atlas_test_euids) and not self.atlas_trf_euid:
107107
raise ValueError("atlas_trf_euid is required when atlas_test_euid is provided")
108108
return self
@@ -115,26 +115,26 @@ class BetaAcceptedMaterialCreateRequest(BaseModel):
115115
container_template_code: str = Field(default="container/tube/tube-generic-10ml/1.0")
116116
status: str = Field(default="active")
117117
properties: dict[str, Any] = Field(default_factory=dict)
118-
atlas_context: AtlasProcessContext
118+
atlas_context: AtlasFulfillmentContext
119119

120120

121121
class BetaTubeCreateRequest(BaseModel):
122122
container_template_code: str = Field(default="container/tube/tube-generic-10ml/1.0")
123123
status: str = Field(default="active")
124124
properties: dict[str, Any] = Field(default_factory=dict)
125-
atlas_context: AtlasProcessContext
125+
atlas_context: AtlasFulfillmentContext
126126

127127

128128
class BetaTubeUpdateRequest(BaseModel):
129129
status: str | None = None
130130
properties: dict[str, Any] | None = None
131-
atlas_context: AtlasProcessContext | None = None
131+
atlas_context: AtlasFulfillmentContext | None = None
132132

133133

134134
class BetaSpecimenUpdateRequest(BaseModel):
135135
status: str | None = None
136136
properties: dict[str, Any] | None = None
137-
atlas_context: AtlasProcessContext | None = None
137+
atlas_context: AtlasFulfillmentContext | None = None
138138

139139

140140
class BetaMaterialResponse(BaseModel):
@@ -179,7 +179,7 @@ class BetaExtractionCreateRequest(BaseModel):
179179
well_name: str
180180
extraction_type: Literal["cfdna", "gdna"] = Field(default="cfdna")
181181
output_name: str | None = None
182-
atlas_test_process_item_euid: str | None = None
182+
atlas_test_fulfillment_item_euid: str | None = None
183183
claim_euid: str | None = None
184184
consume_source: bool = False
185185
metadata: dict[str, Any] = Field(default_factory=dict)
@@ -191,7 +191,7 @@ class BetaExtractionResponse(BaseModel):
191191
well_euid: str
192192
well_name: str
193193
extraction_output_euid: str
194-
atlas_test_process_item_euid: str
194+
atlas_test_fulfillment_item_euid: str
195195
current_queue: str
196196
idempotent_replay: bool = False
197197

@@ -229,7 +229,7 @@ class BetaLibraryPrepCreateRequest(BaseModel):
229229
class BetaLibraryPrepResponse(BaseModel):
230230
source_extraction_output_euid: str
231231
library_prep_output_euid: str
232-
atlas_test_process_item_euid: str
232+
atlas_test_fulfillment_item_euid: str
233233
current_queue: str
234234
idempotent_replay: bool = False
235235

@@ -316,7 +316,7 @@ class BetaRunResolutionResponse(BaseModel):
316316
atlas_tenant_id: str
317317
atlas_trf_euid: str
318318
atlas_test_euid: str
319-
atlas_test_process_item_euid: str
319+
atlas_test_fulfillment_item_euid: str
320320

321321

322322
class BetaClaimCreateRequest(BaseModel):

docs/bloom_beta_api_contracts.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22

33
## Accepted Material Registration
44

5-
Bloom accepts Atlas-approved material only after Atlas records an `ACCEPTED` intake outcome and materializes one or more test process items.
5+
Bloom accepts Atlas-approved material only after Atlas records an `ACCEPTED` intake outcome and materializes one or more test fulfillment items.
66

77
Minimum request context:
88

99
- `atlas_context.atlas_trf_euid`
1010
- `atlas_context.atlas_patient_euid`
11-
- `atlas_context.process_items[].atlas_test_euid`
12-
- `atlas_context.process_items[].atlas_test_process_item_euid`
11+
- `atlas_context.fulfillment_items[].atlas_test_euid`
12+
- `atlas_context.fulfillment_items[].atlas_test_fulfillment_item_euid`
1313
- patient context
1414
- shipment or test kit context
1515
- queue intent
1616
- idempotency key
1717

18-
Bloom persists Atlas linkage through graph-linked reference objects and returns EUID-only identifiers for created material plus `process_item_euids[]`.
18+
Bloom persists Atlas linkage through graph-linked reference objects and returns EUID-only identifiers for created material plus `fulfillment_item_euids[]`.
1919

2020
For accepted-material ingress, queue transitions are expected on `container_euid` (container-first lab movement).
2121

@@ -52,7 +52,7 @@ Output:
5252
- `atlas_tenant_id`
5353
- `atlas_trf_euid`
5454
- `atlas_test_euid`
55-
- `atlas_test_process_item_euid`
55+
- `atlas_test_fulfillment_item_euid`
5656

5757
Rules:
5858

docs/container_first_handoff_execplan.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ Align accepted-material ingestion so queue ingress and cross-system linkage are
1111
1. Atlas bridge and external-object graph updates
1212
- Queue accepted material by `container_euid`.
1313
- Persist container mapping before specimen mapping.
14-
- Keep container relations to TRF/Test/TestProcessItem + Shipment/TestKit.
14+
- Keep container relations to TRF/Test/TestFulfillmentItem + Shipment/TestKit.
1515
- Keep specimen relations to Patient + containment only.
1616

1717
2. Bloom accepted-material behavior updates
1818
- Resolve/create container before specimen creation.
19-
- Attach process-item reference links to container.
19+
- Attach fulfillment-item reference links to container.
2020
- Attach patient-only reference link to specimen.
2121
- Add patient EUID to accepted-material Atlas context contract.
2222
- Add queue-resolution fallback: specimen can resolve queue from containing container.
@@ -33,11 +33,11 @@ Align accepted-material ingestion so queue ingress and cross-system linkage are
3333

3434
## Acceptance Checks
3535
- Atlas accepted-material handoff calls Bloom queue endpoint with `container_euid`.
36-
- Atlas specimen external-object mapping has no test/test-process-item relations.
37-
- Bloom accepted-material creation stores process-item refs on container and patient ref on specimen.
36+
- Atlas specimen external-object mapping has no test/test-fulfillment-item relations.
37+
- Bloom accepted-material creation stores fulfillment-item refs on container and patient ref on specimen.
3838
- Bloom extraction succeeds when only container has ingress queue membership.
3939
- Resolver and downstream beta flow remain unchanged.
4040

4141
## Breaking-Change Notes
4242
- `POST /api/v1/external/atlas/beta/materials` accepted-material context now carries Atlas patient EUID.
43-
- Specimen external-object relation semantics changed: no direct test/test-process-item relation projection.
43+
- Specimen external-object relation semantics changed: no direct test/test-fulfillment-item relation projection.

templates/modern/assays.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{% extends "modern/base.html" %}
22

3-
{% block title %}Assays - BLOOM LIMS{% endblock %}
3+
{% block title %}Queue Runtime - BLOOM LIMS{% endblock %}
44

55
{% block content %}
66
<div class="page-header">
77
<div>
8-
<h1>Assays</h1>
9-
<p class="text-muted">Manage laboratory assay objects</p>
8+
<h1>Queue Runtime</h1>
9+
<p class="text-muted">Queue/material/run-centric operational objects</p>
1010
</div>
1111
<div class="page-actions">
1212
</div>
@@ -15,9 +15,9 @@ <h1>Assays</h1>
1515
<!-- Filter Bar -->
1616
<div class="filter-bar mb-lg">
1717
<div class="filter-group">
18-
<label class="filter-label">Type:</label>
18+
<label class="filter-label">Runtime Type:</label>
1919
<select class="form-select" id="filter-type" onchange="filterAssays()">
20-
<option value="all" {% if show_type == 'all' %}selected{% endif %}>All Types</option>
20+
<option value="all" {% if show_type == 'all' %}selected{% endif %}>All Runtime Types</option>
2121
{% for atype in assay_types %}
2222
<option value="{{ atype }}" {% if show_type == atype %}selected{% endif %}>{{ atype }}</option>
2323
{% endfor %}
@@ -33,12 +33,12 @@ <h1>Assays</h1>
3333
</select>
3434
</div>
3535
<div class="filter-search">
36-
<input type="text" class="form-input" placeholder="Search assays..." id="search-input" onkeyup="searchAssays(this.value)">
36+
<input type="text" class="form-input" placeholder="Search queue runtime objects..." id="search-input" onkeyup="searchAssays(this.value)">
3737
<i class="fas fa-search"></i>
3838
</div>
3939
</div>
4040

41-
<!-- Assays Grid -->
41+
<!-- Queue Runtime Grid -->
4242
<div class="grid grid-3" id="assays-grid">
4343
{% if assays %}
4444
{% for assay in assays %}
@@ -69,7 +69,7 @@ <h4 class="assay-name">{{ assay.name | default(assay.subtype) }}</h4>
6969
<div class="empty-state" style="grid-column: 1 / -1;">
7070
<div class="empty-state-icon"><i class="fas fa-flask"></i></div>
7171
<p>No assays found</p>
72-
<p class="text-muted text-sm">Create a new assay to get started</p>
72+
<p class="text-muted text-sm">Queue runtime objects appear as material and run operations progress</p>
7373
</div>
7474
{% endif %}
7575
</div>

0 commit comments

Comments
 (0)