Eight steps from a CRO folder to a running study. Steps 3 to 6 are live — answer a finding and the ledger moves, the diff grows and the diagnostic changes its mind. Step 3 is the one that matters: it is the moment the data is wrong, and nothing in open.gismo today would tell you.
The contract is real. The 42 consumers, which raw domain each one needs, which
four literal strings the hepatic displays demand, and which single line of
gsm.mapping does the mapping were all read out of the installed packages and
demo-301's own workflow files this morning.
The delivery is constructed — demo-301's own data re-cut the way a CRO and an EDC vendor would send it, so the row counts are real counts of a study that exists. open.gismo has no such screen today; this is a design, not a recording.
HEP-204 is a Phase 2 study with a hepatic safety question. The CRO sends a SAS transport and an event listing; the EDC vendor sends a site list; the labs come from three vendors in two vocabularies. Nothing carries a gsm column name. Nobody is going to rename these files, and any design that assumes they will has lost before it starts.
$ ls -1lh dm.xpt SAS transport 1,005 × 16 subject level, ADaM-flavoured ae_listing.csv EDC export 2,583 × 15 no toxicity grade column lab_central.csv central lab 28,600 × 11 8 analytes lab_specialty.csv specialty lab 7,150 × 10 TBILI, GGT — bilirubin in µmol/L lab_haem.csv haematology 21,450 × 9 no reference range at all sites_20260824.csv EDC export 150 × 9 site list 60,938 rows · 70 columns · no define.xml · no ECG · no deviations $ og init ~/hep-204 ✔ project created at ~/hep-204 config/ 3 files workflows/ 44 workflow YAMLs snapshotted from the installed gsm packages input/ empty Next: put your data in input/, then og_read("~/hep-204") $ cp ~/deliveries/hep204-2026-08-24/* ~/hep-204/input/ $ git -C ~/hep-204 init -q && git -C ~/hep-204 add -A && git -C ~/hep-204 commit -qm "delivery 2026-08-24"
og_validate() answers with twelve lines of file not found and names not
one of the six files that are sitting in the folder. It is looking for
input/Raw_AE.csv, input/Raw_LB.csv and ten more of the same. It
could have opened dm.xpt — haven is installed in this stack and
never called.
> og_validate("~/hep-204")
✖ Raw_AE file not found: input/Raw_AE.csv
✖ Raw_DATACHG file not found: input/Raw_DATACHG.csv
✖ Raw_DATAENT file not found: input/Raw_DATAENT.csv
… nine more …
✖ Raw_SUBJ file not found: input/Raw_SUBJ.csv
The folder contains 60,938 rows of this study. None of them are mentioned.
og_read() opens the folder as delivered and writes one document. It never fails.
Every finding in it is priced in displays — the charts and metrics somebody
asked for — and never in column names, because the person reading this did not choose the
column names and should not have to learn them.
~/hep-204/input · 60,938 rows · 70 columns · 4.1 MB · nothing in this folder was modified
spec: at all —
srs0001, report_kri_site,
report_kri_country. They run over whatever produced results,
so the site risk score's denominator moves with your answers rather than failing.
Nothing on this page can price them, and this line is the only honest thing to do
about it.
| File | Read with | Rows | Cols | What I think it is |
|---|---|---|---|---|
| dm.xpt | haven | 1,005 | 16 | Subject level. ✔ confident USUBJID, SITEID, ARM, one row per participant. |
| ae_listing.csv | read.csv | 2,583 | 15 | Adverse events. ✔ confident Verbatim term, MedDRA, seriousness, start and end dates. |
| lab_central.csv | read.csv | 28,600 | 11 | Laboratory results, 8 analytes. ◐ one of three |
| lab_specialty.csv | read.csv | 7,150 | 10 | Laboratory results, 2 analytes. ◐ one of three |
| lab_haem.csv | read.csv | 21,450 | 9 | Laboratory results, 6 analytes. ⚠ no reference range |
| sites_20260824.csv | read.csv | 150 | 9 | Sites. ✔ confident One row per invid, with country. |
Six of six read, including the transport. Expanding a row gives the column profile: distinct values, type, completeness and two real values from the file.
Sixteen displays will render output that looks entirely normal and is entirely wrong. Zero will render correctly. That ordering — wrong ahead of missing — is the whole argument for this direction: a display that does not render gets noticed, and a display that renders wrongly gets read.
This is the moment the product earns trust or loses it. Every column is present. Every type
checks. og_validate() is all green. og_run() completes without an
error — and sixteen displays report zero at every site while the site risk score quietly
reweights over the ones that survived. Measured on demo-301's own data this morning.
The mapping joins labs and events to participants with an inner join. As delivered, two of the three joins return zero rows. Both columns are present, both are character, both are complete. There is no check anywhere in open.gismo today that would catch this.
What it costs if you run anyway.
A repair is offered as a rule you can read, argue with and re-use next month — not as 765 identifiers somebody silently rewrote.
This session reproduced the failure on demo-301's real
input/Raw_SUBJ.csv and input/Raw_LB.csv: build
USUBJID as studyid-subjid and the overlap with the lab file's bare
identifier is 0 of 765; the inner join the study actually runs returns
0 rows from 57,200. Strip the constant prefix and it is 765 of 765, all
57,200 rows. Two other sessions found the same thing from two other angles the same night.
The grafted rule from the convention direction is the one that matters here: this finding is blocking, not advisory. A key with zero overlap is not a preference.
config/data-config.yaml maps one domain to one file. A liver panel split across
three vendors is currently inexpressible, which is a real limitation of open.gismo v0.2.0 and
not a hypothetical one.
They share a shape — a participant identifier, an analyte name, a numeric result, a visit — and between them they cover the whole panel with no analyte appearing twice.
Disclosing the missing reference range before the run, rather than letting six analytes quietly arrive with null limits, is the intake direction's own rule. What was grafted on is the price: nine displays, named, so the answer is not a preference but a trade.
Three displays do not ask for a column — they ask for four literal strings inside a column. Every column check on the lab files passes. The values were compared instead, and none of the four matched.
The candidates are ordered and never scored. The guided-setup session built a value profiler that scored 9 of 9 on a CRO lab extract where the published name detector managed 3 of 9 — and then scored 1.000 on a wrong answer and 0.849 on a right one against demo-301's own file. There is no threshold that separates those, so the number is never shown and the binding is never applied.
TBILI → Bilirubin is also a clinical assertion about total versus direct
bilirubin. A person clicks it. Nothing here pre-applies it, at any confidence.
Two displays want a toxicity grade the protocol never collected. This is the graft from the mapping bench, and the data-manager lens is the reason it is here: today her spreadsheet says N/A in a cell and that judgement dies there. In December, when somebody asks why there is no Grade 3+ KRI, "the tool didn't ask me" is not an answer she can give.
A blank is indistinguishable from work nobody has got to yet, so next month the product asks again and the month after that as well. A decline carries a reason, a date and a price; the study site prints not collected on this protocol where the tile would have been; and the next read does not raise it. That is the only difference between a tool that records a judgement and one that nags.
There is no product database. Every answer above is text in the study's own files, in the study's own git, reviewed in the tool the reviewer already uses to review things. Go back and change an answer and this grows or shrinks with it.
Nothing written yet. Answer a finding on steps 3 to 6.
Nothing written yet.
The comments survive. demo-301's own LB.yaml opens with a
thirteen-line note explaining that toxgrg_nsv must be cast to character or the
Grade 3+ KRI's WHERE toxgrg_nsv IN ('3','4') matches nothing. open.gismo's
current YAML writer round-trips that file from 80 lines to 70 and from
13 comment lines to 0 — measured this morning. The writer specified here
edits the file as text and re-parses to check it wrote what it meant.
The plain English is kept beside the machine-readable spec.
config/setup-log.md is where the user's own sentence lives, so the person
reading this diff in December can see why, not just what.
Only two keys are ever written into the spec.
gsm.mapping::ApplySpec reads source_col and type and
nothing else. Everything that is not a rename — the affix rule, the analyte vocabulary, the
derived grade — arrives as generated RunQuery steps, which is this design's
admitted weakness rather than a feature. It is shown, not hidden.
og doctor is the diagnostic behind the document: the same findings, ordered the
same way, in the terminal, for the person who never opens a browser. Every finding names the
file, the line and the literal text that fixes it.
$ og doctor ~/hep-204 open.gismo doctor — HEP-204 ~/hep-204 14 domains · 126 columns · 42 displays ✖ 1 blocking the run will not start ⚠ 4 costed the run will start; here is what each one costs ✔ 4 domains ready ────────────────────────────────────────────────────────────── ✖ BLOCKING · identifiers do not join Raw_LB.subjid and Raw_SUBJ.subjid share 0 of 765 sampled values. Raw_AE.subjid and Raw_SUBJ.subjid share 0 of 765 sampled values. Fix — one line, because dm.xpt carries both forms: workflows/1_mappings/SUBJ.yaml:23 subjid: type: character + source_col: SUBJID Or, if the vendors genuinely disagree, the rule: config/vocab/subjid.rules.csv pattern,replacement,domains ^AA-AA-000-0000-,,Raw_SUBJ ^0301-,,Raw_LB Cost if you run anyway: 16 of 42 displays report zero at every site, and srs0001 re-weights over the 3 that ran. Nothing errors. ────────────────────────────────────────────────────────────── og_run() refused. Fix the blocking finding, or --force it and own the result.
$ og doctor ~/hep-204 open.gismo doctor — HEP-204 ~/hep-204 14 domains · 126 columns · 42 displays ✔ 0 blocking the run will start ⚠ costed priced below, biggest first ✔ 4 domains ready Raw_SUBJ Raw_AE Raw_LB Raw_SITE ────────────────────────────────────────────────────────────── ✔ RESOLVED · identifiers 765 of 765 · 57,200 lab rows · 2,583 events workflows/1_mappings/SUBJ.yaml:23 source_col: SUBJID ────────────────────────────────────────────────────────────── ⚠ COSTED · six extracts were not delivered 23 displays EDC audit trail 8 protocol deviations 4 disposition 5 ECG 2 enrolment 2 eligibility 2 acknowledged 2026-08-27 — not raised again ⚠ COSTED · srs0001 declares no spec unpriceable Site Risk Score normalises over the analyses that ran. Your answers move its denominator rather than failing it. I cannot check this, and this line is the only honest thing I can do about it. ────────────────────────────────────────────────────────────── ✔ og_run() will start.
| Display | State | What the tile says |
|---|---|---|
| kri0003 · kri0004 | ✖ not supplied | Protocol deviations were not part of this delivery. |
| qt_explorer · saf0002 | ✖ not supplied | No ECG dataset was delivered. |
| saf0003 | ✖ declined | — |
| srs0001 | ⚠ unpriceable | Site Risk Score is normalised over the analyses that ran, and this study ran a subset. The denominator moved. |
$ cp ~/deliveries/hep204-2026-09-24/* ~/hep-204/input/
$ og_read("~/hep-204")
replaying 11 answers from workflows/ and config/ — every assumption re-measured, not trusted
✔ affix rules still apply 765 → 812 participants, overlap 1.00
✔ 4 analyte bindings still resolve
✔ toxicity grade derivable for 10 of 16 analytes
✔ saf0003 declined 2026-08-27 — not asked again
⚠ 1 new finding
lab_specialty.csv now carries a 17th analyte: "DBILI"
No display asks for it. Nothing breaks. Recorded, and that is all.
✔ ready — og_run() will start
Replay is the one idea on these pages that no direction designed and no session measured. It exists because the data-manager lens said month two costing what month one cost was the worst call in the session, and because the answers are already files, so replay is not a new store. It is the first thing to challenge.