This is the document og_read() would write after being pointed at a six-file CRO delivery — an HTML file in the project at intake/report.html, committed alongside the workflows. It is clickable. Answer a finding and the ledger at the top moves, and the diff at the bottom shows the YAML that answering wrote into the project.
Every number below is measured. The inventory is generated from the actual profile of the actual fixture; the display counts come from resolving demo-301’s 39 metric and module workflows back to the raw columns they consume. Nothing here is illustrative.
~/brightstar · 60,938 rows · 50 columns · 4.08 MB · nothing in this folder was modified
Four things need an answer from you. Each one is priced in the displays it costs, biggest first. Twenty displays are not available at all and there is nothing to answer about them — they are listed at the bottom so you know what they would have been.
3
✔ Will render, complete
Everything these need is present and unambiguous.
6
⚠ Will render, wrong
These will produce output that looks fine and is not. Answering findings 1 to 3 fixes them.
10
◐ Blocked, one answer away
Waiting on a column nothing carries but everything is here to build. Finding 4.
20
✖ Cannot render
Seven extracts were not supplied. No answer here can change this.
Thirty-nine displays, each counted once. The second number is the one that matters. A display that renders wrongly is more expensive than one that does not render, because nobody goes looking for it.
1 · What I found
Every file in the folder, whatever its name or format. Expand a row to see the column profile.
File
Format
Rows
Cols
What I think it is
Read with haven for the transport file and utils::read.csv for the rest. open.gismo v0.2.0 reads only CSV today; haven and arrow are already installed in the stack and never called.
2 · What needs an answer
Ranked by the number of displays affected, not by how confident I am.
✖
Your hepatic explorer will draw an empty chart, and nothing else will tell you
2 displays
The eDISH chart does not ask for a column — it asks for four literal strings inside one. Every column check on your lab files passes. I compared the values instead.
the chart names, in workflows/4_modules/hep_explorer.yaml:
ALT → "Alanine Aminotransferase" TB → "Bilirubin"
AST → "Aspartate Aminotransferase" ALP → "Alkaline Phosphatase"
your three lab files hold 15 distinct analyte names:
lab_central.csv TEST ALB ALKPALTAST CREAT GLUC K
lab_specialty.csv ANALYTE TBILI GGT
lab_haem.csv LBTEST Hematocrit Hemoglobin Lymphocytes …
matched: 0 of 4— the chart renders, with no series on it
units checked: TBILI is umol/L against a umol/L limit; the cut-points
are multiples of the upper limit, so the mismatch is safe. Recorded.
✖
One lab file’s participant IDs do not join to anything
4 displays
The mapping joins labs to participants with an inner join. As delivered this one returns nothing. The column is present, typed, and complete — there is no check anywhere today that would catch it.
1,005 identifiers in dm.xpt USUBJID S1000 · S1001 · S1002
1,005 identifiers in lab_haem.csv SUBJID 0301-S1000 · 0301-S1001 · 0301-S1002
overlap as delivered .......... 0.00(0 of 1,005)
overlap after removing "0301-" 1.00(1,005 of 1,005)
lab_central.csv SUBJECT and lab_specialty.csv PT_ID both join
cleanly at 1.00. This file is the only one with the prefix.
◐
Three files look like one lab domain
all 6
They share a shape — a participant ID, an analyte name, a numeric result, a visit — and between them they cover the whole panel with no analyte appearing twice. data-config.yaml maps one domain to one file today, so this is currently inexpressible.
proposed Raw_LB = lab_central ∪ lab_haem ∪ lab_specialty
= 28,600 + 21,450 + 7,150 = 57,200 rows, 16 analytes, no overlap
reference range present: lab_central yes lab_specialty yes
lab_haem no columns at all→ six haematology analytes will carry a null normal range.
Consequence: they get no CTCAE grade in finding 4, and the
shift plot's reference band is blank for those measures.
Saying this now rather than after the run.
◐
Five columns nothing carries, that I can build from what you sent
10 displays
There is no column to point at for these, so no mapping surface can ask about them. Two of the five are assumptions rather than facts, and both are marked as such below and on every display that uses them.
✔ Raw_AE.aest_dy AESTDTC − RFSTDTC 2,583 / 2,583 clean✔ Raw_AE.aeen_dy AEENDTC − RFSTDTC 2,583 / 2,583 clean◐ Raw_SUBJ.timeonstudy last dated record − RFSTDTC 765 / 765
RFENDTC is empty for all 1,005 rows — you sent no exit date.
"Last dated record" stands in for it. Median 28 d, max 119 d.◐ Raw_LB.toxgrg_nsv CTCAE v5 from result ÷ upper limit
3,575 ALT rows band as G0 2838 · G1 465 · G2 79 · G3 173 · G4 20.
Haematology has no range (finding 3) so gets no grade.✖ Raw_AE.aetoxgr substituted from AESEV 2,583 / 2,583
severity is not toxicity grade. Three levels standing in for five.
Lossy. Every display built on it will carry the mark.
3 · What answering has written
Nothing is written until you answer. This is the whole diff, and it is yours to edit or revert.
Nothing written yet. Answer a finding above.
Column renames are one source_col: line each — an existing, exercised gsm.mapping feature. Prefix strips, analyte bindings and derivations have no declarative home in the spec today and would be written as steps: entries, which is the weakest point in this direction (see W2).
4 · What I cannot build, and why
Twenty displays. No answer available — these need an extract you did not send. Acknowledging records the decision so the next read does not raise it again, and the study site prints “not supplied” where the tile would have been.
EDC audit trail8 displays Query rate and age, data entry lag, data change rate — at site and country level.
Protocol deviations4 displays Important and total PD rates.
Disposition5 displays Study and study-drug completion, plus one quality tolerance limit.
ECG2 displays The QT explorer and the QTc metric.
Eligibility criteria1 display The eligibility-violation tolerance limit.
Live. The three counters, the finding states and the diff all move together — answering finding 4 is the only one that adds displays; the other three convert displays from “renders wrongly” to “renders correctly”, which is the distinction this direction is built around.
What the mock is claiming, and what it is not
The inventory table is rendered from data/brightstar.js, which was written by the profiler running over the real fixture. The row counts, column counts, distinct-value counts and example values are all measured.
The four findings are the four detectors from the direction, each producing its real output on this delivery.
The ledger arithmetic is real and sums to 39: 3 complete, 6 that render wrongly, 10 blocked on a derivable column, 20 with no data at all. Answering everything moves 16 of them into the first column, for 19 rendering and 20 dark. Resolved from the workflow specs, not chosen for the mock.
The diff is illustrative in one respect and honest about it: the source_col: lines are exactly what would be written, and the rest is shown as steps: entries because the spec has no other place to put them. That is the direction’s admitted weakness, shown rather than hidden.
Not claimed: that the analyte confirmations are safe. TBILI to Bilirubin is a clinical assertion about total versus direct bilirubin, and the mock deliberately makes a human click it rather than pre-applying it.