Study Lifecycle Monitor — Field Guide

Biostatistics Operationsoperating-depth wiki How to read and act on every panel of Study_Lifecycle_Monitor.html — the operational complement to the participant-safety TRIALMON_Dashboard.html. Operations-only · decision-support, not validated software · demo data is simulated.

1What this is — and what it is not

The Study Lifecycle Monitor (Study_Lifecycle_Monitor.html) is an at-a-glance operational console for a biostatistician watching a portfolio of early-phase studies at the program / operational level. It answers one practical question across all eight studies at once: which studies are slipping on the things biostatistics owns — milestones, data readiness, TLF production, and double-programming — and which one deserves my attention first this week?

Who it is for

The intended user is the biostatistician (or biostatistics lead) coordinating deliverables across multiple concurrent studies. It is built for triage and conversation: open it before a portfolio stand-up, scan the KPI strip and heatmap, click into the one or two studies that are red, and walk in already knowing where the operational pressure is. Every score it shows is computed from operational metadata only and is fully transparent — the exact formulas and thresholds are printed inside the dashboard itself.

What it is — and what it is not

This is the operational / program complement to the participant-safety dashboard, TRIALMON_Dashboard.html. The two cover different worlds and do not overlap:

ConcernThis dashboard (Study Lifecycle Monitor)TRIALMON (companion)
WatchesDeliverables, data readiness, TLF production, lifecycle milestonesParticipant-safety monitoring
LevelProgram / operational, study-aggregateParticipant-level clinical signals
QuestionAre we on track to deliver?Is anyone at risk?

Boundaries — read these before you rely on anything here.

  • Decision-support, not validated software. The scores are a triage aid to direct attention; humans decide. Nothing here is a regulated output.
  • Operations-only — no PHI, no participant-level data, no reported clinical numbers. Only read-only operational metadata (query counts, percent-clean, milestone dates, TLF stage counts) is ever in scope.
  • Reported / regulated values come only from validated tools — Phoenix WinNonlin, Pinnacle 21, the EDC/CTMS of record, and the TLF program tracker. This dashboard never sources a number that goes into a deliverable.
  • The demo data is SIMULATED. Eight studies (CP-101…CP-108) are generated from a fixed seed, reproducible run to run. It is a seeded demonstration, not real study data — note the SIMULATED DEMO DATA badge in the header and footer.

Layout, top to bottom

The page reads as a single top-to-bottom triage flow. A header carries the Biostatistics Operations badge, the SIMULATED DEMO DATA badge, and a study-focus selector that drives every per-study panel. Below it:

  1. KPI strip — six portfolio counters (active studies, at-risk, on-watch, open queries, average deliverables complete, next DB lock).
  2. Portfolio health heatmap — a study × dimension RAG matrix (Timeline, Data, TLF, Resource), sorted worst-first; click any cell to drill in.
  3. Operational early-warning signals — the ranked list of fired alerts, red-first (top nine shown).
  4. Portfolio trend — an 8-week two-line trend (open queries vs. TLFs finalized) across all studies, each line on its own scale.
  5. Selected-study panels — milestone track, data readiness, TLF production funnel, and the full-width deliverables pipeline, all driven by the focused study.
  6. Study roster — a sortable table of all eight studies; click a row for the detail modal.

A per-study detail modal opens from either a heatmap cell or a roster row, and a footer repeats the honesty and governance disclaimer.

How to read color anywhere on the page. Every RAG cell, bar, and pip uses one consistent scale on a 0–100 score: green = on track, amber = watch, red = at risk.

green if score ≤ 33 · amber (watch) if 33 < score ≤ 66 · red (at risk) if score > 66

This same banding governs the heatmap cells, the KPI at-risk/on-watch counts, and the per-dimension scores, so once you have it, you can read the whole console.

The portfolio is deliberately seeded so one study, CP-108, is forced into an at-risk Reporting state (DB lock past due, TLF production far behind, milestones overdue). That study becomes the initial focus when the page loads, so you land on a worked at-risk example rather than an empty calm — useful when demonstrating the console or learning its panels.

2Quick start — a 5-minute weekly review

This is the fastest defensible way to read Study_Lifecycle_Monitor.html once a week. The dashboard is decision-support, not autopilot: it ranks where your operational attention should go, but a human decides every action. Work the steps top-to-bottom — each one narrows the portfolio from "all 8 studies" down to "the one I act on first."

Everything you see here is SIMULATED DEMO DATA (seeded, reproducible). It is operational metadata only — no PHI, no participant-level data, no reported clinical numbers. Any regulated or reported value (PK parameters, datasets, TLF numbers) comes only from validated tools such as Phoenix WinNonlin, Pinnacle 21, and the EDC/CTMS — never from this dashboard. This is not validated software.

The 5-minute loop

1. Scan the KPI strip (top, #kpis) — 30 seconds. Read the six counters left-to-right for a portfolio pulse. The two that should stop you are colored cells: At risk (count of studies with overall >66; red if any) and On watch (count with overall in 33–66; amber if any). Also note Avg deliverables complete (green if >70, amber if >45, else red) and Next DB lock — it names the nearest study, e.g. Next DB lock (CP-108), turns amber when <14 days out, and shows — if no study has a positive countdown. If "At risk" reads 0 and nothing is amber, you can move faster through the rest.
2. Read the RAG heatmap for red/amber rows (#heat) — 60 seconds. Rows are already sorted by overall risk descending, so the top row is your worst study. Scan the four dimension columns — Timeline, Data, TLF, Resource — and find the red (>66) and amber (33–66) cells. A red cell tells you not just which study but which dimension is driving its risk, so you can triage by failure mode (e.g. a red Data cell = query/cleaning problem; a red TLF cell = output production lag). Note the worst 1–2 studies and the specific red dimension for each.
Cell color (ragColor): red if value >66, amber if 33–66, green if ≤33. Cell value = rounded dimension score. Overall = 0.30·Timeline + 0.28·Data + 0.27·TLF + 0.15·Resource, clamped to [0,100].
3. Read the early-warning signals (#signals) — 60 seconds. This ranked list turns scores into specific, actionable alerts; red signals sort first and only the top 9 show. Each line gives a severity pip, the study id (who), a title, and a detail message. Map each red signal to its type so you know the action:
  • DB lock at risk — open queries with the lock close (e.g. "N open queries with DBL in D days"); red when <10 days.
  • Query backlog rising — net query growth >10 over the last 3 weeks (e.g. "+qd open queries over the last 3 weeks"); red when growth >25.
  • TLF production behind — finalized fraction below 60% while in Reporting/DB Lock (e.g. "X% finalized while in Reporting"); red when below 35%.
  • Double-programming lag — independent-QC programming behind in reporting (always amber).
  • Milestone overdue — lists the past-due milestone keys (always red).
If you see "No operational signals above threshold." the portfolio is quiet — confirm against the heatmap and wrap up.
Tip: cross-check the heatmap against the signals. A red heatmap cell with no matching signal means the dimension score is high but no specific trigger fired yet — worth a glance, lower urgency. A red signal always points at a concrete, named condition you can act on this week. Optionally glance at Portfolio trend (#trend): the amber line is portfolio open queries and the blue line cumulative TLFs finalized over 8 weeks — confirms whether things are getting better or worse.
4. Drill into the worst study via the modal (#modal) — 90 seconds. Open the per-study detail dialog two ways: click the offending red cell in the heatmap (this focuses the modal on that exact dimension and shows "focus: dim" in the header), or click the study's row in the roster (#tbl, sortable — click a header to re-sort, default is overall risk descending). The modal shows the study header (id — name; design · phase · Lead) and an 8-tile grid: Overall risk with its on-track/watch/at-risk label, the four dimension scores, Enrollment (enrolled/target + sites), Open queries · clean · SDV, Days to DB lock, the TLF cascade (planned→programmed→qcd→finalized), and double-programmed % · discrepancies. Read the tiles that match the red dimension to confirm the root cause. The open "Where these numbers would come from" note ties each metric to its real source — milestones→CTMS, enrollment/queries/clean/SDV→EDC, TLF funnel→TLF program tracker, deliverables→Protocol→CSR register — so you know which validated system to query for the authoritative figure. Close with × or a backdrop click.
5. Confirm against the selected-study panels and note actions (#data, #funnel, #ms, #deliv) — 60 seconds. Selecting a study (via modal entry point or the header #studySel) re-renders the lower panels for it. Use them to pin down the action: Data readiness for the open-query sparkline and the days-to-DB-lock status line (amber when 0<days<21; otherwise "DB lock not scheduled/reached"); the TLF production funnel for where output is stuck in the planned→programmed→QC→finalized pipeline; the Milestone track for any red (past-due) dots; and the Deliverables pipeline for the lagging phase (Startup/Conduct/Analysis/Reporting). Write down a concrete next step per red study — e.g. "CP-108: DBL overdue, TLF finalized 10% in Reporting → escalate to lead, pull TLF tracker for true counts" — and assign an owner. The dashboard surfaces; you decide and act.

What "done" looks like

After the loop you should have: the count of at-risk and on-watch studies, the worst study and its driving dimension, the specific signal(s) behind it, the validated source you'll query for authoritative numbers, and one written action with an owner. In the seeded demo, CP-108 is deliberately forced at risk (Reporting phase, DB lock 24 days overdue, ~10% of TLFs finalized, milestones past due), so it rises to the top row of the heatmap and is the default focus — a good worked example to practice the loop on.

Scope reminder: this monitor watches deliverables, data readiness, TLF production, and milestones — the operational/program view. Participant-safety signals live in the companion TRIALMON_Dashboard.html, not here. Run both in a weekly review for full coverage.

3The data model & the SIMULATED banner

Everything you see in Study_Lifecycle_Monitor.html is generated from a single deterministic seed at page load. There is no server, no file read, and no network call. The portfolio is built once, scored once, and rendered. This section explains exactly what that synthetic portfolio contains, why it looks the way it does, and how each made-up field maps to a real source so you can read the dashboard correctly and brief others without overclaiming.

Read this first. Every number on this dashboard is SIMULATED DEMO DATA — a seeded, reproducible fiction. It is not real study data. It carries no PHI, no participant-level data, and no reported clinical numbers. The dashboard is decision-support for operational triage; it is not validated software and it never sources a regulated value. Reported and regulated numbers come only from validated tools (Phoenix WinNonlin for PK, Pinnacle 21 for conformance, your EDC/CTMS of record). To replace the fiction with live data, see the wiring section.

The SIMULATED banner — three places, one promise

The honesty disclosure is repeated in three locations so it cannot be missed on a screenshot or a printout:

  • Header badge — an amber SIMULATED DEMO DATA pill next to the title (alongside the Biostatistics Operations badge).
  • Footer (#foot) — the full disclaimer: self-contained, offline, SIMULATED DEMO DATA (seeded, reproducible) — not real study data; operational monitoring only (no PHI, no participant-level data, no reported clinical numbers; reported values always come from validated tools); transparent operational scoring — no predictive model and no experimental components.
  • README — the same statement plus the "Deliberately excluded" list.

The per-study modal repeats a fourth, narrower version of the promise in its provenance note: "Operational fields only — no PHI, no participant-level or reported clinical values."

The seed and the PRNG

The portfolio is driven by a small splitmix-style 32-bit pseudo-random generator. One line creates it and three helpers wrap it:

R = rng(20260621) // splitmix-style 32-bit PRNG, seeded with today's date as an integer ri(a,b) → integer in [a,b] rf(a,b) → float in [a,b) pick(arr) → one element of arr TODAY = new Date('2026-06-21')

Because the seed is fixed at 20260621 (the build date, 2026-06-21, read as an integer), every reload produces the identical portfolio: same study list, same scores, same signals, same heatmap. This is what "reproducible" means here — two people on two machines see exactly the same demo. TODAY is likewise frozen to 2026-06-21, so all the day-count math (days-to-DB-lock, overdue milestones) is stable rather than drifting with the wall clock.

To reshape the demo change the seed argument in rng(20260621) to get a different-but-still-reproducible portfolio, or widen/narrow the genStudy ranges (the ri/rf/pick calls) to change the character of the studies. The seed is the only thing that determines what you see — there is no hidden state.

How many studies, and how they are built

The portfolio is exactly 8 studies, generated in a loop (for i in 0..7: genStudy(i)) and given sequential ids CP-101 through CP-108 (101 + i). Each genStudy(i) call returns one canonical study object — the same shape consumed by every panel and by the go-live adapter. The KPI strip's "Active studies" counter is simply STUDIES.length, which is why it reads 8.

The four phases and phase-conditioned realism

Every study sits in one of four lifecycle phases:

PHASES = ['Startup', 'Conduct', 'DB Lock', 'Reporting'] phase = PHASES[ min(3, floor(i/2) % 4 + (R() < .3 ? 1 : 0)) ]

The index i spreads studies roughly evenly across phases, with a 30% chance of nudging one phase later — so the eight studies cover the whole protocol-to-CSR span rather than clustering. The point of phase is that it conditions almost every other field, so the demo behaves like a real portfolio instead of uniform noise. The engine first draws a hidden lifecycle-progress fraction prog (0 to 1) from a phase-specific band, and then derives the visible metrics from it:

FieldHow phase shapes it
prog (hidden progress)Startup rf(.05,.25); Conduct rf(.3,.6); DB Lock rf(.6,.8); Reporting rf(.8,.98)
Enrollmenttarget = pick([24,32,40,48,60,16]); enrolled = Startup 0–40% of target, Conduct 50–100%, otherwise full
Open queries (data.openQ)Startup ri(0,6); Conduct ri(25,120); DB Lock ri(10,48); Reporting ri(0,8) — backlog peaks mid-conduct, drains toward reporting
Query trend (qtrend)8-week back-cast ending at openQ (qtrend[7] = openQ); Reporting phase trends downward (queries closing)
Clean % / SDV %cleanPct = min(.999, prog*0.7 + rf(.2,.32)); sdvPct = min(.999, prog*0.6 + rf(.2,.35)) — later studies are cleaner
TLF funnelplanned = pick([90,120,150,180,72]); a reporting proxy by phase drives the programmed → qcd → finalized cascade, so output ramps only as a study approaches reporting
Deliverablesper-phase totals {Startup:14, Conduct:26, Analysis:34, Reporting:20} with descending stage weights [1, .85, .55, .2] so later deliverable phases lag earlier ones

Milestones are likewise derived from prog: a base date of TODAY − (prog*240 + 30) days, offsets [0,18,35,150,185,215,250] for the seven keys (Protocol, SAP, FPFV, LPLV, DBL, TLFs, CSR), each marked done if its date has passed (with 92% reliability), wip if due within the next 14 days, risk if past due and not done, else todo. A study with prog > 0.9 force-completes its first five milestones. daysToDBL is then computed straight from the DBL milestone's date — which is what every "days to DB lock" readout and the DB-lock-pressure scoring term consume.

The deliberately at-risk study: CP-108

A demo of an operational monitor is useless if nothing is ever red. So after the eight studies are generated, the last one — CP-108 (STUDIES[7]) — is deliberately forced into a clearly at-risk state. This is a hard-coded override, not a draw of the RNG:

CP-108 forced state: phase = 'Reporting' daysToDBL = -24 (DB lock 24 days OVERDUE) tlf.programmed = 0.45 × planned tlf.qcd = 0.22 × planned tlf.finalized = 0.10 × planned prog.dblProgPct = 42 prog.discrep = 16 data.openQ = 7 data.cleanPct = 0.91 data.sdvPct = 0.86 milestones 'TLFs' and 'CSR' → status 'risk'

This profile is a textbook crisis: a study past its DB-lock date, deep into Reporting, with only 10% of TLFs finalized, double-programming behind, and two overdue milestones. It deliberately exercises the red end of several rules at once:

  • Heatmap — CP-108's Timeline, TLF, and Resource cells go red, lifting its overall risk above all peers.
  • Early-warning signals — it fires "TLF production behind" (10% finalized while in Reporting, red), "Double-programming lag" (42%, amber), and "Milestone overdue" (TLFs, CSR past due, red). Note it does not fire "DB lock at risk": that signal only triggers when daysToDBL is positive (an upcoming lock), and CP-108's lock date is already in the past (−24).
  • KPI strip — it is the study counted in "At risk," turning that cell red.

Because the dashboard's initial focus (SEL) is set to the study with the highest overall score after sorting, the forcing also guarantees the app opens focused on CP-108 — every selected-study panel (milestone track, data readiness, TLF funnel, deliverables, modal) lands on the crisis study first, which is exactly what you want a triage tool to do.

Reading the demo correctly. CP-108's red is engineered, not discovered. When you walk someone through the dashboard, say so plainly: "I planted one failing study so you can see what an alert looks like." The other seven studies are honest random draws within phase-realistic bands; CP-108 is the scripted worst case.

What each synthetic field maps to in reality

The fiction is structured so that going live is a field-for-field swap — the synthetic generator and the real-data adapter return the identical study object shape. Each panel already documents its real source (the modal's "Where these numbers would come from" note states it explicitly). The mapping is:

Demo fieldReal source of record
id, phase, milestones, enrolled/target, sitesCTMS
openQ, qtrend[8], cleanPct, sdvPct, daysToDBLEDC
tlf.planned / programmed / qcd / finalizedTLF program tracker
deliv {phase: {done, total}}Protocol→CSR deliverables register
prog.dblProgPct, prog.discrepTLF tracker / programming metadata

All of these are read-only operational metadata — counts, percentages, dates, statuses. None of them is a participant-level record or a reportable clinical value, by design. The wiring section walks through loadPortfolio(), the async stub that replaces genStudy with reads from those five sources.

Using the data model in practice

1. Open Study_Lifecycle_Monitor.html. Confirm the amber SIMULATED badge is present — if it ever isn't, stop and check what data is loaded before trusting a number.
2. Note that the app opens on CP-108. That is the planted at-risk study; it is your demo's "this is what trouble looks like."
3. To show the demo is deterministic, reload the page — the portfolio is identical because the seed is fixed. Use this to reassure a reviewer that nothing is being generated on the fly per session.
4. To narrate phase realism, walk a Startup study and a Reporting study side by side: queries high in Conduct and draining by Reporting, deliverables lagging in later phases, TLF output ramping only near reporting. These are the phase-conditioned bands, not coincidence.
5. When asked "is this real?", answer with the mapping table above: every field has a named source of record (CTMS / EDC / TLF tracker / deliverables register), and going live is a read-only swap — not a rebuild.
Do not present any demo value as a study fact. A clean % of 91% on CP-108 is fiction generated from a seed; the real figure lives in the EDC and the reportable analysis numbers live in validated tools. The dashboard flags where to look and what to triage — a human decides, and the authoritative number comes from the system of record.

4KPI strip

The KPI strip is the top row of Study_Lifecycle_Monitor.html — six counters that summarize the entire portfolio (all 8 studies, CP-101 through CP-108) in one glance before you read any panel below it. Each counter shows a large value over a small label. Four of the six are color-coded green/amber/red; two are neutral counts. Nothing here is study-specific — the strip ignores the focus selector and always aggregates across the whole portfolio. Read it left to right as a triage: how many studies, how many in trouble, how heavy is the data-cleaning load, how far along is the work, and what is the next hard deadline.

The six counters

KPIWhat it counts / aggregatesHow to read it
Active studiesThe portfolio size — STUDIES.length, fixed at 8 in the demo.Denominator for everything else. Neutral (no color). If you wire a live source, this becomes your live study count.
At riskNumber of studies whose overall risk score exceeds 66 (sc.overall > 66).Red whenever the count is 1 or more; green only when it is 0. A non-zero red here is your first action item — go to the heatmap and find the red rows.
On watchNumber of studies in the watch band: overall score above 33 and at or below 66 (33 < sc.overall ≤ 66).Amber whenever the count is 1 or more; green when 0. These are not emergencies but are trending the wrong way — review before they cross 66.
Open queries (portfolio)Sum of every study's open-query count (Σ s.data.openQ across all 8).Neutral total — the portfolio-wide data-cleaning backlog. Rising week over week means EDC cleanup is falling behind somewhere; the Portfolio trend panel shows the direction and the roster shows which studies own the queries.
Avg deliverables completeMean across studies of each study's completion ratio. For each study: (sum of deliv.done) ÷ (sum of deliv.total) over its four phases; that per-study ratio is averaged across all 8 and shown as a rounded percent.Green if >70%, amber if >45%, red otherwise. This is an unweighted average of ratios, not a portfolio-wide done/total — a small study counts as much as a large one. Treat it as overall program momentum, not a precise completion figure.
Next DB lockThe smallest positive days-to-DB-lock across the portfolio, shown as Nd with the owning study id in the label, e.g. Next DB lock (CP-104).Amber if that value is under 14 days; otherwise neutral. Shows — when no study has a future DB lock. This is your nearest immovable deadline — pair it with the "DB lock at risk" early-warning signal.

How the risk bands behind two of the counters are defined

"At risk" and "On watch" both key off each study's overall risk score, so the same threshold ladder used everywhere on the dashboard governs these two counters.

rag(v): green if v ≤ 33  ·  amber (watch) if 33 < v ≤ 66  ·  red (at risk) if v > 66. The overall score is a weighted blend, clamped to [0,100]: overall = 0.30×Timeline + 0.28×Data + 0.27×TLF + 0.15×Resource.

Because the bands are mutually exclusive, a study is counted in exactly one of "At risk" or "On watch" (or neither, if green). The two counters never double-count the same study.

The strip is your 5-second read. A healthy portfolio shows "At risk" and "On watch" both green/zero, "Avg deliverables complete" green, and "Next DB lock" with comfortable headroom. The demo deliberately forces CP-108 into the at-risk state (it is a past-due Reporting-phase study), so on first load you will see "At risk" red with a count of 1 — that is expected and exists to exercise the red state and the signals below.
The two color-coded count cells fire on any non-zero count — there is no "how many is too many" gradient. One at-risk study turns the cell red just as five would. Use the count value, not just the color, to gauge breadth; then drill into the heatmap or roster to see which studies.
"Avg deliverables complete" is an average of per-study completion ratios, so it can read reassuringly high even when one study is badly behind — a single struggling study is diluted by seven healthy ones. Never let a green here override a red "At risk" cell. The strip is a portfolio overview; act on the worst study, not the average.
Every number in this strip is SIMULATED, seeded demo data (reproducible via the fixed seed) — it is not real study data. The dashboard is operations-only: it carries no PHI, no participant-level data, and no reported clinical numbers. Regulated and reported values always come from validated tools (Phoenix WinNonlin, Pinnacle 21, the EDC/CTMS of record) — never from this strip. This is decision-support, not validated software: it points you where to look, and people make the call.

Working the strip

1. Scan the two band counters first. If "At risk" is red, that study is your priority — open the heatmap, find the red row, and click into the per-study modal.
2. If "At risk" is green but "On watch" is amber, you have no emergencies but at least one study is trending into the watch band — schedule a review before it crosses 66.
3. Read "Open queries (portfolio)" against last week. A climbing total means data cleaning is slipping; confirm the direction in the Portfolio trend panel and identify the owner in the roster.
4. Check "Next DB lock". If it is amber (under 14 days), jump to that study (named in the label) and confirm its query backlog and double-programming are on track to clear before lock.
5. Use "Avg deliverables complete" only as a momentum gauge for the whole program — never as a substitute for inspecting the worst individual study.

5Portfolio RAG heatmap & the scoring model

The #heat panel is the heart of Study_Lifecycle_Monitor.html: a study-by-dimension RAG matrix that compresses every study in the portfolio into four colored scores plus an overall. It is the first place to look each morning and the launch pad for every drill-in. This section explains exactly what each cell means, the arithmetic behind it, and — critically — what the colors do and do not entitle you to conclude.

How to read the matrix

Each row is one study (label = s.id, e.g. CP-104; hover for the full s.name). Rows are sorted by overall risk descending, so the study most in need of attention sits at the top. Each of the four columns is a scored dimension:

  • Timeline — milestone slippage and pressure from the approaching database lock.
  • Data — open-query burden, data-cleaning completeness, and source-data verification, amplified near DB lock.
  • TLF — how far the table/listing/figure pipeline is from finalized, weighted by reporting proximity.
  • Resource — independent double-programming progress and open programming discrepancies.

Every cell shows Math.round(s.sc[dim]) — an integer 0–100 — painted by the same RAG rule used everywhere in the dashboard:

ragColor(v): red if v > 66 · amber if 33 < v ≤ 66 · green if v ≤ 33
(equivalently rag(v) returns 'r' when v>66, 'a' when v>33, else 'g')

Higher is worse: the score is a risk number, not a completion percentage. A green Timeline cell of 8 means little slippage; a red Data cell of 74 means the query/clean/SDV picture is poor with the lock bearing down. The inline collapsible note directly under the heatmap quotes these same thresholds (<33 green / 33–66 watch / >66 at risk) so the model is never hidden from the reader.

The four sub-scores, input by input

Every dimension is built from named operational inputs and then passed through clamp(...), which bounds the result to [0,100]. The exact formulas are reproduced from the dashboard's scoring object below. Two shared multipliers appear repeatedly:

dblPressure(s): Startup = .15; otherwise by daysToDBL d → d ≤ 0 : 1 · d < 14 : .85 · d < 30 : .6 · d < 60 : .35 · else : .2
repPressure(s): Reporting = 1 · DB Lock = .6 · Conduct = .25 · else .1

Timeline

Inputs: count of milestones whose status is 'risk' (overdue and not done), plus dblPressure(s).

Timeline = clamp( overdue × 22 + dblPressure(s) × 45 )

Each overdue milestone adds 22 points; the DB-lock proximity term contributes up to 45. So a single overdue milestone with a lock more than 60 days out scores 22 + .2×45 ≈ 31 (green), while two overdue milestones at/after the lock date scores 44 + 45 = 89 (red).

Data

Inputs: s.data.openQ, s.data.cleanPct, s.data.sdvPct, and dblPressure(s).

Data = clamp( ( min(1, openQ/120) × 45 + (1 − cleanPct) × 30 + (1 − sdvPct) × 25 ) × ( 0.5 + 0.6 × dblPressure(s) ) )

Open queries saturate their 45-point contribution at 120 open queries; incomplete cleaning and incomplete SDV add up to 30 and 25 respectively. The whole bracket is then scaled by lock pressure — the identical data picture reads materially worse in a study days from lock than one in early Conduct.

TLF

Inputs: s.tlf.finalized, s.tlf.planned, repPressure(s).

TLF = clamp( ( 1 − finalized / planned ) × 100 × ( 0.3 + 0.8 × repPressure(s) ) )

The fraction not yet finalized drives the raw score; reporting proximity scales it. A study with 10% of TLFs finalized in Reporting (repPressure = 1) scores 90 × 1.1 = 99 (red); the same 10% in Startup (repPressure = .1) scores 90 × 0.38 ≈ 34 (amber) — early studies aren't punished for not having finalized outputs yet.

Resource

Inputs: s.prog.dblProgPct (double-programmed %), s.prog.discrep (open discrepancies), repPressure(s).

Resource = clamp( ( 1 − dblProgPct / 100 ) × 55 + min(1, discrep / 18) × 45 × repPressure(s) )

Lagging independent (double) programming contributes up to 55 unconditionally; open discrepancies add up to 45 but only matter as reporting nears, via repPressure.

The overall score

The matrix sort key and the KPI/roster risk swatch all come from a single weighted blend of the four dimensions:

overall = clamp( 0.30 × Timeline + 0.28 × Data + 0.27 × TLF + 0.15 × Resource )

Timeline carries the most weight, Data and TLF nearly as much, Resource the least. The same green/amber/red thresholds apply to overall, which is what defines the portfolio counters: At risk = studies with overall > 66, On watch = 33 < overall ≤ 66.

Because the weights sum to 1.00 and every dimension is clamped to [0,100], overall is itself bounded to [0,100] and reads on the same scale as the cells around it. A row can show a green overall while still carrying one red dimension cell — always scan across the row, not just the rightmost value.

Click-to-drill

The heatmap is the primary navigation surface. Clicking any cell does three things at once: it sets SEL to that study, opens the per-study modal (#modal) focused on the clicked dimension (the modal subline shows focus: dim), and re-renders the selected-study panels — Milestone track, Data readiness, TLF funnel, Deliverables pipeline — to that study. So a red Data cell is one click from the open-query sparkline and days-to-DB-lock line that explain it.

1. Scan the top rows — they carry the highest overall risk.
2. Read across each row to find which dimension(s) are red, not just the overall.
3. Click the offending cell to open the modal focused on that dimension.
4. Use the modal's provenance note and the re-rendered detail panels to trace the score back to its operational inputs.
5. Confirm any regulated/reported figure in the validated source tool before acting — never quote a dashboard number into a deliverable.

Where the inputs come from at go-live

In the demo every input is synthetic, but the data adapter maps each one to a read-only operational source: milestones and phase from CTMS; openQ, qtrend, cleanPct, sdvPct, daysToDBL from the EDC; the TLF funnel counts from the TLF program tracker; deliverables from the Protocol→CSR register; and dblProgPct/discrep from programming metadata. Only operational metadata crosses — no PHI, no participant-level data, no reported clinical values.

The scoring model is a transparent operational heuristic, not a validated metric. The weights (0.30 / 0.28 / 0.27 / 0.15), the per-input point caps, and the 33/66 thresholds are chosen for sensible triage, not derived or statistically calibrated against outcomes. There is no predictive model behind any cell — a score is a weighted snapshot of current operational inputs, nothing more. Treat the colors as a prompt to look, not a verdict: this is decision-support, the human decides. The dashboard is not validated software, and any number destined for a regulated deliverable must come from the validated tool of record (Phoenix WinNonlin, Pinnacle 21, the EDC/CTMS), never from this screen.
All demo figures are SIMULATED, seeded, reproducible synthetic data (rng(20260621)) — useful for learning the panel and exercising the green/amber/red spread, but not real study values. This panel is the operational/program complement to TRIALMON_Dashboard.html, which covers participant-safety monitoring; the Study Lifecycle Monitor deliberately watches deliverables, data readiness, TLF production, and milestones only.

6Operational early-warning signals

The Operational early-warning signals panel (#signals) is the dashboard's triage list. It scans all eight studies in the portfolio, fires any signal whose trigger condition is met, then ranks the results red-first and shows the top nine. Each row carries a severity pip (red r or amber a), the study id that owns it (who), a short title (t), and a detail message (m). When nothing crosses threshold you see the empty state: "No operational signals above threshold."

Read these top-down. The list is sorted so that every red signal appears before every amber one ((b.sev==='r')-(a.sev==='r')), then sliced to nine. A short, all-amber list is a good day; a list that is full and red-led is your morning standup agenda.

There are exactly five signal types. They are deliberately distinct: a DB-lock signal is about the lock date colliding with open data, a query-backlog signal is about the trend regardless of lock date, the two TLF/programming signals are about output readiness near reporting, and the milestone signal is about a date that has simply passed. A single study can fire several at once — that is informative, not redundant.

The five signals at a glance

SignalTriggerWhat it meansAction & owner
DB lock at risk daysToDBL>0 && daysToDBL<21 && openQ>15. Red if daysToDBL<10, else amber. The database lock is within three weeks but a meaningful query backlog is still open. Message reads "N open queries with DBL in D days." The lock is jeopardized by data that is not yet clean. Data management drives query resolution and confirms the cleaning runway; PM decides whether to hold or move the lock date. Biostatistician/lead confirms the SAP-driven analysis can still run on the locked snapshot. Red = escalate today.
Query backlog rising qd = qtrend[7]-qtrend[4] > 10 (net open-query growth over the last 3 weeks). Red if qd>25, else amber. The open-query count is trending the wrong way — issues are being raised faster than they are being closed. Message reads "+qd open queries over the last 3 weeks." A worsening data-cleaning trajectory, independent of any lock date. Data management investigates the source (new edit checks, a problem site, a late data transfer) and rebalances reviewer load. Lead tracks whether the trend reverses week-over-week. Red trend warrants a root-cause check, not just more reviewers.
TLF production behind repPressure(s)>=.6 (phase is Reporting or DB Lock) && finalized/planned < .6. Red if finalized/planned<.35, else amber. The study is at or near reporting, yet fewer than 60% of planned TLFs are finalized. Message reads "X% finalized while in <phase>." Output production is lagging given how close the deliverable deadline is. Biostatistician/lead owns: re-sequence the TLF program, confirm SAP-final shells, and check whether the bottleneck is programming, QC, or sign-off. PM re-checks the CSR timeline. Red here is the strongest output-risk flag near reporting.
Double-programming lag repPressure(s)>=.6 && dblProgPct < 60. Severity amber (fixed). Near reporting, independent (double) programming is under 60% complete. Message reads "P% double-programmed, K open discrepancies." The independent-QC arm is behind, so finalized outputs may lack full verification. Biostatistician/lead reallocates the QC programmer and works the open discrepancy list to zero before sign-off. This is the QC-coverage companion to "TLF production behind" — clear both before declaring any TLF finalized.
Milestone overdue At least one milestone with status risk (past due date, not done). Severity red (fixed). One or more of the seven lifecycle milestones (Protocol, SAP, FPFV, LPLV, DBL, TLFs, CSR) is past its due date without completion. Message lists the overdue milestone keys joined by ", " followed by "past due." PM owns the milestone timeline and replans; the milestone's natural owner acts (e.g. biostatistician for SAP/TLFs/CSR, data management for DBL, sites/CRA for FPFV/LPLV). Always red — confirm whether the date slipped or the status update is simply stale.
Severity rules, verbatim: DB lock at risk — red if daysToDBL<10, else amber. Query backlog rising — red if qd>25, else amber. TLF production behind — red if finalized/planned<.35, else amber. Double-programming lag — always amber. Milestone overdue — always red.

How the signals relate to the scores

Signals are discrete fired alerts; the heatmap dimension scores are continuous risk levels. They draw on overlapping inputs but answer different questions. A study can carry a high Timeline score (driven by overdue*22 + dblPressure*45) while only the milestone signal has actually fired; conversely a query trend can fire "Query backlog rising" before the Data score crosses 66. Use the signal list to know what to act on now and the heatmap to know which study is heaviest overall.

Cross-reading tip: when "DB lock at risk" and "Query backlog rising" fire on the same study, that is the highest-confidence data-cleaning emergency — the backlog is both large near the lock and still growing. When "TLF production behind" and "Double-programming lag" co-fire near reporting, the output is behind and under-verified; do not let finalized counts climb until QC catches up.

Working the panel: a triage pass

1. Scan the red pips first (they are sorted to the top). Note each who study id and the milestone/lock named in the detail message.
2. For each red signal, open that study's modal (click the roster row or a heatmap cell) to see the underlying numbers — days to DB lock, open queries, the TLF funnel (planned→programmed→qcd→finalized), and double-programmed % with discrepancy count.
3. Route by owner using the table above: data management for query/DB-lock signals, biostatistician/lead for TLF and double-programming signals, PM for milestone replanning.
4. Confirm against the source system before acting — a "Milestone overdue" can be a genuine slip or just a stale CTMS status; an "X% finalized" can lag the real TLF program tracker.
5. On the next refresh, check whether amber trends (especially "Query backlog rising") are reversing. A trend that keeps climbing despite added effort needs a root-cause review, not more reviewers.
The dashboard flags; humans decide. These signals are operational decision-support computed from simulated, seeded demo data — they are not real study data and this is not validated software. No PHI or participant-level clinical data is involved. Any reported or regulated number (PK parameters, validated dataset counts, finalized TLF values) comes only from the validated tools — Phoenix WinNonlin, Pinnacle 21, the EDC/CTMS and the TLF program tracker — never from this panel. Treat every fired signal as a prompt to look, confirm in the source system, and decide.

For participant-safety signals (DLT triggers, lab trajectories, AE patterns), this panel is silent by design — that is the role of the companion TRIALMON_Dashboard.html. The Study Lifecycle Monitor (Study_Lifecycle_Monitor.html) watches deliverables, data readiness, TLF production, and milestones only.

7Milestone track

The Milestone track panel (#ms, with the heading #msTitle) is the selected study's lifecycle timeline. It draws the seven standard milestones in left-to-right order as a row of colored dots, each labelled with its key and its date. The panel title shows the selected study id and its current phase (for example, CP-104 — Conduct). It is driven entirely by the currently selected study (SEL), which you change from the heatmap, the roster, or the header study-focus selector.

The seven milestones, in order

The sequence is fixed (the MS_KEYS list) and is the same for every study. Each step renders its key (m.k) and its date (m.d, formatted YYYY-MM-DD):

  1. Protocol — protocol finalized/approved. The lifecycle's first gate.
  2. SAP — Statistical Analysis Plan signed off.
  3. FPFV — First Participant First Visit (enrollment opens).
  4. LPLV — Last Participant Last Visit (enrollment/visits complete).
  5. DBL — Database Lock. This is the pivot milestone: its date also drives daysToDBL, the data-readiness clock, and the Timeline dimension's lock pressure.
  6. TLFs — Tables, Listings, and Figures finalized.
  7. CSR — Clinical Study Report. The final lifecycle gate.

How each dot is colored (status)

Every milestone carries a status (m.s) that maps to a dot color class. Read the dots left to right; you should see green giving way to a single blue "you are here" marker, then panel-gray for work still ahead. A red dot anywhere is the thing to act on.

StatusDot classColorMeaning
donedonegreenCompleted — the due date passed and the milestone was met.
wipwipblue accentIn progress — due within the next 14 days (and not before today). This is the active front of the study.
riskriskredOverdue — the due date has passed and the milestone is not done.
todotodopanel grayNot yet started — the due date is in the future.
Status assignment (per milestone, against TODAY = 2026-06-21), evaluated in this order:
done if the milestone is completed (due date < TODAY and met);
else wip if the due date falls within the next 14 days (TODAY ≤ d < TODAY+14);
else risk if the due date is past (d < TODAY) and the milestone is not done;
else todo (the due date is still in the future).
When lifecycle progress prog > 0.9, the first five milestones (Protocol → DBL) are forced to done.
Milestone dates are spaced from a per-study baseline (TODAY − (prog×240 + 30) days) using fixed day-offsets [0, 18, 35, 150, 185, 215, 250] for Protocol → CSR. So the gap from FPFV to LPLV is the longest stretch, and the final three (DBL → TLFs → CSR) cluster near the end — that compression is expected, not a defect.

Reading slippage: on-track vs at-risk vs overdue

"On track" is the absence of red plus a clean left-to-right progression: a green run, exactly one blue wip marker at the current front, then gray todo dots. You read slippage from where the red appears and how far right the green has reached:

  • A red dot early in the sequence (e.g. SAP or FPFV) means a foundational gate is overdue — everything downstream inherits that delay.
  • A red dot at DBL is the most consequential: it means the lock date has passed unmet, which also drives daysToDBL ≤ 0 and pushes the Timeline dimension toward red.
  • Red at TLFs and/or CSR means reporting deliverables are past due — pair this with the TLF production funnel and deliverables pipeline to see how far behind output really is.
  • If a wip (blue) dot sits to the left of a green done dot, that is an out-of-order signal worth questioning with the study team.
A single red milestone here fires the portfolio "Milestone overdue" early-warning signal (severity red, fixed). Its message lists every overdue milestone key joined by ", " plus " past due" — so the #signals panel and this track always agree on which keys are red. Each overdue milestone also contributes +22 to that study's Timeline dimension score (overdue × 22), which can tip the heatmap cell amber or red.

How to use this panel

1. Select the study — click its heatmap row/cell, click its roster row, or use the header study-focus selector. The panel re-renders to that study and the title updates to id — phase.
2. Find the blue wip dot — that is the current active milestone. Everything green to its left is complete; everything gray to its right is upcoming.
3. Scan for red. Any red dot is overdue. Note its position in the sequence to judge how foundational the slip is, and read its date to see how long it has been past due.
4. Cross-check the consequence. If DBL is red or wip, open Data readiness (#data) for the daysToDBL clock and open-query load; if TLFs/CSR are red, open the TLF funnel (#funnel) and deliverables pipeline (#deliv).
5. Confirm against the source of record before acting. Milestone dates and statuses shown here are simulated demo values; the authoritative dates live in CTMS.
Provenance: in a live deployment this panel is fed read-only from CTMS via ctmsStudies() — supplying id, phase, and the milestones array (Protocol → CSR, each with its date and status), alongside enrolled/target and sites. The per-study modal's provenance note states the same mapping (Milestones → CTMS).
This panel is operations/decision-support only — it is not validated software. The dates and statuses are SIMULATED DEMO DATA (seeded, reproducible — not real study data); it carries no PHI and no participant-level data. Use it to spot where attention is needed; humans decide, and any regulated milestone fact is confirmed in CTMS and the validated tools, never taken from the dashboard. Companion file TRIALMON_Dashboard.html covers participant-safety monitoring; Study_Lifecycle_Monitor.html (this dashboard) covers operational/program status only.

8Data readiness

The Data readiness panel (#data) summarizes the database-cleaning state of the selected study in four rows: a clean % bar, an SDV % bar, an open-query sparkline with a current count, and a one-line days-to-DB-lock status. Together these answer one operational question: is this database on track to lock clean and on time? Switch the selected study with the header focus select, a heatmap cell, or a roster row.

Everything in this panel is operational metadata, not clinical results. Clean %, SDV %, query counts and DBL date describe the state of the database, not any participant outcome. No PHI and no participant-level data appear here. In the live build these fields come read-only from EDC (edcMetrics(s.id)); reported and regulated numbers always come from validated tools (Pinnacle 21, Phoenix WinNonlin, EDC/CTMS), never from this dashboard. The demo data is SIMULATED (seed rng(20260621)).

Clean % bar

Clean % (d.cleanPct) is the share of the database judged clean — entered, edit-checked, and free of open issues. It is shown as a filled bar whose width equals the percentage and whose color is inverted: the panel colors the bar by ragColor((1-cleanPct)*100), so the bar is the RAG color of how much is not clean.

Clean bar width = pct(cleanPct). Clean bar color = ragColor((1 - cleanPct) * 100) → red when cleanPct < 0.34, amber when 0.34 ≤ cleanPct < 0.67, green when cleanPct ≥ 0.67.

Read the color, not just the length: a 60%-clean bar is amber (40% unclean), and only at roughly 67% clean and above does it turn green. The number printed to the right is the raw clean %.

SDV % bar

SDV % (d.sdvPct) is source-data-verification coverage — the share of data verified against source. Same bar idea, but with a stricter color rule: ragColor((1-sdvPct)*120). The ×120 multiplier (vs ×100 for clean) means SDV must be higher than clean % to reach the same color band.

SDV bar width = pct(sdvPct). SDV bar color = ragColor((1 - sdvPct) * 120) → red when sdvPct < 0.45, amber when 0.45 ≤ sdvPct < 0.725, green when sdvPct ≥ 0.725.
Because of the ×120 factor, an SDV bar can read amber at a percentage where the clean bar would read green. That is intentional: with risk-based monitoring, SDV is allowed to trail clean, so the panel sets a higher bar (literally) before it calls SDV green.

Open-query sparkline and count

The Open queries row shows two things: an 8-week sparkline of d.qtrend and the current count d.openQ. By construction qtrend[7] equals the current openQ, so the rightmost point of the line is today; the line reads oldest (left) → newest (right), one point per week.

The sparkline is normalized to its own min and max ((v-mn)/(mx-mn) over the 8 weeks). It shows trend shape, not absolute level — a study with 4 queries and one with 110 queries can produce visually similar lines. Always read the sparkline together with the number beside it: the number gives the level, the line gives the direction.

The count turns amber when openQ > 40 (otherwise muted). For interpreting the shape: a line trending down toward the right is a database closing out; a line trending up is a growing backlog. A rising backlog is also caught independently by the Query backlog rising early-warning signal, which fires when the net change over the last three weeks qtrend[7] - qtrend[4] > 10 (red if > 25).

Healthy ranges are phase-dependent. During Conduct, dozens of open queries are normal and a flat or rising line is expected as data flows in. During Reporting / DB-Lock, the line should be falling toward single digits — a flat or rising line that close to lock is the concerning case.

Days to DB lock

The status line reports the gap between today and the DBL milestone (s.daysToDBL, derived from the DBL milestone date). It reads "N days to DB lock" when DBL is in the future, and otherwise "DB lock not scheduled" (Startup) or "DB lock reached" (date passed). It is colored amber only when 0 < daysToDBL < 21 — i.e. lock is within three weeks; otherwise it is muted.

DBL line text: daysToDBL > 0 → "daysToDBL days to DB lock"; else "DB lock not scheduled" (Startup) / "DB lock reached". Amber when 0 < daysToDBL < 21.

How the four fields interact

None of these rows is decisive on its own — readiness is the combination of cleaning state and time pressure. The dashboard encodes exactly that interaction in the Data dimension score, which multiplies the cleaning gaps by proximity to lock (dblPressure):

Data score = clamp( (min(1, openQ/120)*45 + (1-cleanPct)*30 + (1-sdvPct)*25) * (0.5 + 0.6*dblPressure(s)) ), where dblPressure rises as DBL nears: d≤0→1, d<14→.85, d<30→.6, d<60→.35, else .2 (Startup is a flat .15).

Two readings of this formula:

  • Same gaps, different timing. A given open-query backlog and unclean fraction count for far more when DBL is days away than when it is months away — the (0.5 + 0.6*dblPressure) factor roughly doubles the score as lock approaches. A messy database is tolerable early and alarming late.
  • Worst case: low clean % close to DBL. High (1-cleanPct) and high (1-sdvPct) multiplied by a near-1 dblPressure drive the Data score toward red, and Data carries 0.28 weight in the overall risk score — so this single situation can push the whole study to At risk.
The pairing to watch is a still-amber/red clean or SDV bar plus an amber days-to-DB-lock line plus a rising query sparkline. That is the canonical "we are about to lock a dirty database" pattern. It also lights up the DB lock at risk signal, which fires when daysToDBL is between 1 and 20 and openQ > 15 (red if daysToDBL < 10), with the message "N open queries with DBL in D days".
1. Glance at the days-to-DB-lock line first. If it is amber, lock is within three weeks and the other rows carry more weight.
2. Check the clean % and SDV % bar colors (not just lengths). Remember SDV is judged on a stricter scale, so an amber SDV bar near an amber DBL line is an action item.
3. Read the open-query sparkline direction together with the count: falling toward single digits near lock is good; flat or rising near lock is the backlog that jeopardizes the lock.
4. Cross-check against the Operational early-warning signals (DB lock at risk, Query backlog rising) and confirm the actual query/clean figures in the EDC before acting.
This panel is decision-support, not autopilot, and the tool is not validated software — it flags where to look, and a human confirms the underlying counts in the source systems. For participant-safety signals (not data-readiness), use the companion TRIALMON_Dashboard.html; this Study_Lifecycle_Monitor.html watches operational readiness only.

9TLF production funnel

The TLF production funnel panel (#funnel) shows, for the currently selected study, how its tables, listings, and figures move through production from planning to sign-off. It is a four-stage funnel of horizontal meter bars, each scaled against the number of Planned outputs so you can read attrition between stages at a glance. To populate it for a study, set the header study-focus <select> (#studySel), click a heatmap cell, or click a roster row — all three set SEL and re-render this panel.

The four stages

The funnel walks the standard output-production path. Each meter's width is 100 * value / planned, and the fill is a blue gradient (#2b6cb0 → #4ea8ff):

StageFieldMeaning
Plannedt.plannedTotal TLF shells/outputs scoped for the study (the funnel's 100% reference). Drawn from the SAP and TLF shells.
Programmedt.programmedOutputs that have a first (production) program written and producing output.
QC passedt.qcdOutputs that have cleared independent QC (e.g. double-programming reconciliation resolved).
Finalizedt.finalizedQC-passed and signed off. The panel note states verbatim: Finalized = QC-passed & signed off.
The funnel mirrors the per-study TLF program tracker — it is a portfolio-level read of the same counts, not an independent source. The funnel does not produce or alter any output; it only reflects stage counts. The actual outputs are produced by validated programming, and any regulated/reported numbers inside those TLFs originate from validated tools (Phoenix WinNonlin for PK/NCA, Pinnacle 21 for conformance checks, the EDC/CTMS of record), never from this dashboard.

Reading a healthy vs. bottlenecked funnel

Because every bar is scaled to planned, a healthy study shows four bars stepping down gently — each stage slightly shorter than the one above it, with Finalized not far behind Programmed. A bottleneck appears as a cliff: one bar is much shorter than the bar immediately above it, marking the stage where work is piling up.

  • Many Programmed but few QC-passed (Programmed bar long, QC passed bar abruptly short): a QC bottleneck. Production is outrunning independent QC capacity — outputs are written but not getting reconciled/verified.
  • QC-passed but few Finalized (QC passed long, Finalized short): a sign-off bottleneck. Verified outputs are waiting on review/approval, not on programming.
  • Planned but little Programmed (only the top bar is long): production has not really started — expected early in a study, a concern late in one.
1. Select the study (header #studySel, or click into it from the heatmap/roster).
2. Scan the four bars top-to-bottom and find the largest single drop between adjacent stages — that adjacent pair brackets the bottleneck stage.
3. Read the gap between Finalized and Planned: that is the remaining work to ship, in proportion to the whole.
4. Cross-check the operational early-warning signals panel (#signals) and the per-study modal for the matching alerts (below) to confirm whether the lag is flagged given the study's phase.

How the funnel drives the TLF risk dimension and signals

The funnel's counts feed the TLF heatmap dimension. The score is weighted by reporting proximity, so the same finalized fraction is benign early and serious late:

TLF = clamp( (1 − finalized/planned) × 100 × (0.3 + 0.8 × repPressure(s)) )
repPressure: Reporting = 1, DB Lock = .6, Conduct = .25, else .1
rag(v): green ≤ 33, amber (watch) 33 < v ≤ 66, red (at risk) v > 66; clamp bounds to [0,100]

Two early-warning signals fire directly off funnel-related counts, and only when the study is near reporting (repPressure(s) ≥ .6, i.e. phase is Reporting or DB Lock):

TLF production behind — fires when repPressure(s) ≥ .6 AND finalized/planned < .6.
Red if finalized/planned < .35, else amber. Message: "X% finalized while in <phase>".
Double-programming lag — fires when repPressure(s) ≥ .6 AND prog.dblProgPct < 60.
Severity amber (fixed). Message: "P% double-programmed, K open discrepancies".
A short QC-passed bar and a "Double-programming lag" signal point at the same root cause from two angles: independent QC programming is behind. prog.dblProgPct (double-programmed %) and prog.discrep (open discrepancies) belong to the Resource dimension, so a QC bottleneck in the funnel often shows up as elevated Resource and TLF scores together in the heatmap row.

Worked reference: the forced at-risk study

The demo deliberately forces CP-108 into an at-risk reporting state, which makes its funnel the textbook QC/sign-off bottleneck. Its counts are programmed = 0.45 × planned, qcd = 0.22 × planned, finalized = 0.10 × planned, with dblProgPct = 42 and phase = Reporting. Read top-to-bottom, the bars fall 100 → 45 → 22 → 10: production already lags, QC roughly halves what was programmed, and almost nothing is signed off — while the study is in Reporting. Because finalized/planned = 0.10 < .35 with repPressure = 1, the "TLF production behind" signal fires red, and dblProgPct = 42 < 60 fires the amber "Double-programming lag" signal. This is the pattern to recognize: a steep cliff at the QC stage on a study that is out of runway.

Upstream production assets

The funnel is the monitoring view; the outputs themselves come from the standardized TLF program template libraries that supply the production code for each shell — the SAS TLF pseudocode/template library and the parallel R (pharmaverse) TLF template library. Those libraries are organized per design (parallel, crossover, single-sequence, SAD, MAD) and are the assets that move counts from Planned into Programmed; the dashboard simply tallies their progress through QC and sign-off via the TLF program tracker.

The funnel reflects SIMULATED, seeded demo data in this build — it is decision-support, not validated software, and a human owns every conclusion. On go-live, this panel reads planned / programmed / qcd / finalized from the TLF program tracker as read-only operational metadata only; no PHI, no participant-level data, and no reported clinical values cross into the dashboard. For the participant-safety counterpart, see TRIALMON_Dashboard.html; this panel lives in Study_Lifecycle_Monitor.html and is operational/program monitoring only.

10Deliverables pipeline by phase

The Deliverables pipeline panel (#deliv) is the dashboard's program-management lens: for the selected study it shows how many deliverables are done versus total in each of four lifecycle phases. It is a scaled echo of the Protocol-to-CSR deliverables register, so reading it well means reading the shape of the whole study lifecycle, not just a single bar. This section also covers the two portfolio-level readouts that aggregate deliverables across all eight studies: the Avg deliverables complete KPI and the Portfolio trend, plus how to find the crunch points.

What the panel shows

The card is full-width and holds one bar per phase, in lifecycle order. The four phase keys (s.deliv) are fixed:

  • Startup — Protocol, SAP, CRF/eCRF design, randomization spec, analysis-dataset specs and the early planning deliverables.
  • Conduct — deliverables produced while the study is enrolling and running: data-management plan execution, interim/DSMB outputs, ongoing programming specs.
  • Analysis — the analysis-stage deliverables: ADaM datasets, the bulk of the TLF program, derivations and define documentation.
  • Reporting — the closeout deliverables: final TLFs, statistical report inputs and CSR contributions.

For each phase the bar is rendered as follows:

bar width = pct(done / total)  ·  bar color = ragColor((1 − done/total) × 90)  ·  label = "done/total"

Note the color polarity: the RAG color is driven by the incomplete fraction. A phase that is mostly finished (high done/total) yields a small (1 − ratio)×90 value and reads green; a phase that is barely started reads red. The ×90 factor means a phase has to be almost untouched (roughly under 27% complete) before it tips red, and below about 63% complete before it goes amber — phases that are well underway stay green.

How to read it phase-by-phase

The synthetic generator deliberately lags later phases behind earlier ones: each phase's completion is pulled down by its position in the lifecycle (an increasing per-phase lag term plus a stage-weight divisor and small noise), so Startup tracks closest to the study's overall progress while Reporting trails furthest. The expected, healthy picture for a study still in its early-to-middle life is therefore a left-to-right downward staircase: Startup fullest, Reporting emptiest. That is normal and not by itself a problem.

Read the panel relative to the study's phase, not in absolute terms. A Reporting-phase study with an empty Reporting bar is a real crunch point; a Conduct-phase study with an empty Reporting bar is simply work that has not started yet. Always pair this panel with the milestone track (#ms) and the study's phase shown in #msTitle to decide whether a low bar is a concern.

Per-phase deliverable counts (register scale)

The four phases are sized from a fixed register so the demo mirrors the 136-item Protocol→CSR register at scale. Totals per phase are fixed:

PhaseRegister total (per study)Stage weightTypical completeness as study matures
Startup141.00Fills first; usually green by Conduct
Conduct260.85Climbs through the conduct phase
Analysis340.55Largest bucket; green only near/after DB lock
Reporting200.20Fills last; expected red until Reporting phase

The done count for each phase is min(total, round(total × clamp₀₁(prog / weight − index×0.15 + noise))), where prog is the study's overall lifecycle progress (0..1), index is the phase position (Startup 0 → Reporting 3), clamp₀₁ bounds the completion fraction to [0, 1], and noise = rf(−0.05, 0.05). The combination of the divisor and the increasing index×0.15 lag term is what pushes later phases down relative to prog. Because Analysis is the largest bucket (34) and sits mid-lifecycle, it is usually the phase that signals the analysis-readiness squeeze before DB lock — watch it climb as prog rises.

Reading overall portfolio progress

Two readouts roll deliverables up to the whole portfolio.

Avg deliverables complete (KPI strip, #kpis) is the headline number:

Avg deliverables complete = mean over the 8 studies of (Σ done / Σ total) × 100, rounded  ·  green if >70, amber if >45, else red

This averages each study's overall deliverable completion (summing done and total across all four phases first, then taking the ratio), then averages those eight study-level percentages. Because it pools all phases, a portfolio dominated by early-phase studies will read lower even when every individual study is on track — interpret it together with the phase mix in the roster, not as a standalone grade.

Portfolio trend (#trend) plots the cumulative TLFs finalized line (blue, #4ea8ff) alongside the open-queries line (amber, #e6b450) across 8 weeks. The deliverables register itself is not trended week-by-week; the finalized-TLF line is the closest proxy for reporting-deliverable velocity. The legend shows current values q[7] and f[7] (newest week).

The TLF line in the portfolio trend is a synthetic ramp, not a stored historical series — f[w] = Σ round(finalized × (0.35 + 0.65·w/7)) across the 8 weeks. It illustrates direction of travel for the demo only. With real data wired in (see go-live), this would be replaced by the actual finalized-TLF time series from the TLF program tracker. Do not read week-over-week TLF deltas as a measured rate in the demo.

Finding the crunch points

A crunch point is a study whose remaining deliverable load is concentrated in a phase that is due now. Use this workflow:

1. Scan the roster (#tbl), sorted by Risk descending (default). The riskiest studies float to the top.
2. For each high-risk study, check its phase column. A study in Reporting or DB Lock is where deliverable lag matters most.
3. Open the study (click the row or a heatmap cell) and read the Deliverables pipeline bars. A red Reporting or Analysis bar on a late-phase study is the crunch point — the work that must close before CSR.
4. Cross-check the milestone track (#ms) for any risk (red) milestone dots — TLFs and CSR milestones flag overdue reporting deliverables directly.
5. Confirm against the early-warning signals (#signals): a TLF production behind signal (fires when phase is Reporting or DB Lock and finalized/planned < 0.6) corroborates an empty Reporting bar near the lock/report date.
In the seeded demo, CP-108 is deliberately forced into an at-risk Reporting state (phase Reporting, DB lock 24 days past, TLFs and CSR milestones set to risk). It is the canonical crunch-point example: late phase, but its reporting-side deliverables and TLF funnel are far behind. Use it to learn the read; it is not a real study.

How this ties to scoring

The deliverables panel is descriptive — it is not a direct input to the four risk dimensions. The TLF and Resource dimensions are driven by the TLF program tracker and programming metadata (finalized/planned, double-programmed %, open discrepancies), not by the register counts. So the deliverables bars give you the program-management context behind a study's risk score without double-counting it. When a study scores red on Timeline or TLF, the deliverables pipeline tells you which lifecycle phase is carrying the unfinished work.

Where the numbers come from

In the demo every deliverable count is SIMULATED, generated deterministically from the seed rng(20260621) and reproducible run to run. At go-live the panel is fed read-only by the Protocol→CSR deliverables register via deliverablesRegister(s.id), returning {phase: {done, total}} for Startup / Conduct / Analysis / Reporting; only operational metadata crosses — no participant-level data.

This dashboard is operations-only decision-support and is not validated software. It carries no PHI, no participant-level clinical data, and no reported clinical numbers. Any reported or regulated value — finalized PK parameters, statistical outputs, dataset conformance — comes only from validated tools (Phoenix WinNonlin, Pinnacle 21, the EDC/CTMS and the TLF program tracker), never from this panel. Deliverable counts here track workflow status, not deliverable content. Humans decide; the dashboard only surfaces where to look.

For participant-safety signals — clinical and laboratory monitoring at the participant level — use the companion TRIALMON_Dashboard.html. This Study Lifecycle Monitor (Study_Lifecycle_Monitor.html) is the operational/program complement: it watches deliverables, data readiness, TLF production and milestones, not participant-level clinical signals.

11Study roster & the drill-down modal

The Study roster (#tbl) and the per-study detail modal (#modal / #modalBox) are the bottom of the funnel: the heatmap and signal panels tell you which study and which dimension deserves attention, the roster lets you triage all eight studies in one sortable table, and the modal gives you the full per-study picture before you act. This section covers how to sort and triage the roster, exactly what the modal shows, and the recommended drill-down path from a colored cell or fired signal to a defensible operational decision.

The roster table: columns and what each one tells you

The roster lists all eight studies (CP-101 through CP-108) with nine columns. It opens sorted by overall risk descending (sortKey='overall', sortDir=-1), so the most at-risk study sits at the top on load — in the demo that is the deliberately forced at-risk study CP-108.

ColumnFieldHow to read it
Studys.idThe study identifier (CP-101…CP-108). Click anywhere in the row to open the modal.
Phases.phaseOne of Startup, Conduct, DB Lock, Reporting. Drives the reporting/DBL pressure weights in scoring.
Designs.designThe study design label.
Leads.ownerThe responsible owner — your first call when a row is red.
Enrolledenrolled/targetRecruitment progress against target.
Open Qs.data.openQOpen EDC queries. Cell turns amber when openQ > 40.
Cleans.data.cleanPctPercent of data clean.
TLF finalfinalized/plannedFinalized TLFs against planned — the headline output-production ratio.
Risks.sc.overallRAG swatch (rag(overall)) plus the rounded overall score. The triage column.

Sorting and triage

Click any column header to sort by that column; clicking the same header again toggles the direction (if(sortKey===k) sortDir*=-1; else { sortKey=k; sortDir=-1; }). Use the columns as a triage lens rather than reading rows top-to-bottom:

  • Risk (default) — start here. Reds first, ordered worst-to-best, gives you the portfolio's escalation queue in one glance.
  • Open Q — sort descending to find the studies driving the portfolio query backlog; amber cells (>40) jump out.
  • TLF final — sort ascending to surface reporting-phase studies whose output production is lagging.
  • Phase — group Reporting and DB Lock studies together, since those are where TLF and double-programming pressure bite hardest.
  • Lead — group by owner when you are preparing a per-lead status conversation.
The Risk column and the heatmap share the same RAG thresholds: rag(v) returns 'g' (green, on track) when v ≤ 33, 'a' (amber, watch) when 33 < v ≤ 66, and 'r' (red, at risk) when v > 66.
A red swatch in the roster and a red cell in the heatmap mean exactly the same thing. But a study can carry a green overall while still hiding one red dimension — always confirm in the modal or heatmap before concluding "fine."

Opening the modal: entry points

The same modal opens from two places, and the entry point changes what the header tells you:

  • From a roster row — click any row. The modal opens on that study with no dimension focus.
  • From a heatmap cell — click a cell. This sets SEL to that study, re-renders the selected-study panels (milestones, data readiness, TLF funnel, deliverables), and opens the modal with a focus dimension noted in the header subline (e.g. focus: Data), telling you which column's color sent you there.

You can also set the selected study from the #studySel dropdown in the header bar, which re-renders all selected-study panels but does not open the modal.

What the modal shows for a single study

The modal (#modalBox) has three parts: a header, an eight-tile key-value grid, and a provenance note.

Header. id — name on the top line; a subline reading design · phase · Lead owner, with · focus: dim appended if you arrived from a heatmap cell.

Eight KV tiles — the full operational snapshot in one view:

TileShows
Overall riskRounded overall score plus its label (on track / watch / at risk).
Timeline / DataThe two dimension scores side by side.
TLF / ResourceThe other two dimension scores.
Enrollmentenrolled/target plus number of sites.
Open queries · clean · SDVEDC data-readiness trio in one tile.
Days to DB lockdaysToDBL (negative means the planned lock date has passed).
TLF cascadeplanned → programmed → qcd → finalized counts.
Double-programmed % · discrepanciesprog.dblProgPct and prog.discrep — the independent-QC programming status.

Provenance note ("Where these numbers would come from") is always open and maps each block to its real source: milestones → CTMS; enrollment, queries, clean, SDV → EDC; the TLF funnel → the TLF program tracker; deliverables → the Protocol→CSR register.

Close the modal with the × button or by clicking the backdrop.

Recommended drill-down workflow

1. Scan the heatmap or signals. A red heatmap cell or a red early-warning signal names the study and the problem dimension. This is your trigger.
2. Click the red cell (not just the row). This focuses SEL on the study, re-renders the selected-study panels, and opens the modal with the focus: dim note so you know which dimension drove the color.
3. Read the KV tiles to see whether one dimension is isolated or several are elevated. Cross-check the focus dimension's tile against its inputs — e.g. a red Data score against the Open queries · clean · SDV tile, or a red TLF score against the TLF cascade tile.
4. Confirm the operational drivers in the dedicated selected-study panels the cell-click just refreshed: the milestone track for overdue keys, data readiness for the query sparkline and days-to-DBL, and the TLF funnel for where the pipeline stalls (programmed vs. qcd vs. finalized).
5. Identify the owner from the modal subline or the roster Lead column, then act through your normal channel — query-resolution push, TLF QC reprioritization, double-programming catch-up, or a milestone escalation.
6. Use the roster to widen the lens. Re-sort by the same problem column (e.g. Open Q descending) to check whether the issue is isolated to one study or a portfolio-wide pattern needing a shared response.
Every number in the roster and modal is SIMULATED, seeded demo data — not real study data. The dashboard is operations-only: it carries no PHI, no participant-level data, and no reported clinical numbers. It is decision-support to help you triage and prioritize, not validated software and not a source of record. Any reported or regulated value — PK parameters, dataset conformance, or anything in a deliverable — comes only from validated tools (Phoenix WinNonlin, Pinnacle 21, the EDC/CTMS systems of record). A human always makes the call; treat a red cell as "go look," never as "the answer."
This Study Lifecycle Monitor watches the program — deliverables, data readiness, TLF production, and milestones. Participant-safety signals live in the companion TRIALMON_Dashboard.html. When a row here looks operationally fine but you need the clinical-safety view, that is the dashboard to open instead.

13Wiring it to live data

Out of the box, Study_Lifecycle_Monitor.html is fully self-contained: it generates all eight studies (CP-101 through CP-108) from a seeded pseudo-random generator, rng(20260621), so every reviewer sees the identical green/amber/red spread. Every number on the screen is SIMULATED DEMO DATA. This section explains the single seam where you swap that synthetic generator for your real operational systems — the DATA ADAPTER — and documents exactly which read-only source feeds each panel and field.

Illustrative until wired. Nothing on this dashboard is a reported, regulated, or submission-grade number, and it never will be. Reported/regulated values come only from validated tools (Phoenix WinNonlin, Pinnacle 21, your validated EDC/CTMS). This monitor is decision-support for triage and conversation; humans decide. It is not validated software.

The one seam: loadPortfolio()

The whole dashboard reads from one array, STUDIES. In demo mode that array is built by calling genStudy(i) eight times. The DATA ADAPTER comment block holds an async stub, loadPortfolio(), whose only job is to return that same array of study objects — same field names, same shape — but populated from your live systems instead of the generator. Go-live is a one-line swap:

STUDIES = await loadPortfolio(); // replaces: STUDIES = Array.from({length:8}, (_,i)=>genStudy(i))

Because every panel already reads the canonical study-object shape that genStudy returns, you do not touch any panel code. You only implement the four source readers and the merge function inside loadPortfolio().

Four read-only sources, one merge

The adapter pulls from four operational systems and merges their fields into the demo's field names. Each reader is keyed by study id so the dashboard stays organized per study:

  • ctmsStudies() — CTMS: returns id, phase, the seven milestones (Protocol..CSR with dates and status), enrolled/target, and sites.
  • edcMetrics(s.id) — EDC: returns openQ, qtrend[8], cleanPct, sdvPct, daysToDBL.
  • tlfTrackerCounts(s.id) — TLF program tracker: returns planned, programmed, qcd, finalized.
  • deliverablesRegister(s.id) — Protocol→CSR deliverables register: returns {phase:{done,total}} for Startup/Conduct/Analysis/Reporting.

A small merge helper, mapToModel(e, t, d), folds the EDC, TLF, and deliverables payloads onto the CTMS skeleton and produces the canonical study object the panels expect.

Go-live transport. Wire each source read-only — either a REST pull at page load or a nightly CSV export that loadPortfolio() parses. Read-only is the contract: the dashboard observes operational metadata and never writes back to CTMS, EDC, the tracker, or the register.

Which source feeds each panel

This is the authoritative mapping. Build your readers to deliver exactly these fields and every panel lights up correctly.

PanelSourceFields
Milestone track + Portfolio health (Timeline dimension)CTMSid, phase, milestones (Protocol..CSR with dates/status), enrolled/target, sites
Data readiness + Data dimensionEDCopenQ, qtrend[8], cleanPct, sdvPct, daysToDBL
TLF production funnel + TLF dimensionTLF program trackerplanned, programmed, qcd, finalized
Deliverables pipelineProtocol→CSR deliverables register{phase:{done,total}} for Startup / Conduct / Analysis / Reporting
Resource dimensionTLF tracker / programming metadataprog.dblProgPct (double-programmed %), prog.discrep (open discrepancies)
Provenance in the product. This same mapping is surfaced to users inside the per-study modal's "Where these numbers would come from" note: Milestones→CTMS · enrollment/queries/clean/SDV→EDC · TLF funnel→TLF program tracker · deliverables→Protocol→CSR register. The wiring is self-documenting on screen, not buried in code.

What must NOT cross the seam

The adapter carries operational metadata only. The fields above are counts, percentages, dates, and statuses — never the underlying clinical record.

No PHI. No participant-level data. No reported clinical numbers. Nothing keyed to a participant crosses into the dashboard — no USUBJID/SUBJID records, no lab values, no AEs, no PK results, no demographics. The EDC reader sends the aggregate open-query count and clean/SDV percentages, not the queries or the case-report-form contents behind them. Reported and regulated values are produced and signed off in validated tools; the dashboard only watches the operational shadow of that work.

Implementation steps

1. Implement ctmsStudies() to return one record per active study with id, phase, the seven dated milestones, enrolled/target, and sites. This is the skeleton everything else attaches to.
2. Implement edcMetrics(id), tlfTrackerCounts(id), and deliverablesRegister(id) as per-study read-only pulls returning only the aggregate fields in the table above.
3. Confirm qtrend is an 8-element series whose last element equals current openQ (qtrend[7] = openQ), so the open-query sparkline and the "Query backlog rising" signal read correctly.
4. Inside loadPortfolio(), loop the studies and call mapToModel(e, t, d) to merge each source payload into the canonical field names.
5. Swap the one line: STUDIES = await loadPortfolio(). Leave every panel untouched.
6. Remove the synthetic seed and the SIMULATED badge only once live data is verified end-to-end; until then, leave the badge visible so no one mistakes demo data for real status.

Sanity checks after wiring

  • The KPI strip reconciles to your sources: Open queries (portfolio) equals the sum of each study's EDC openQ; Active studies equals your live study count.
  • The TLF funnel never shows a later stage exceeding an earlier one — confirm finalized ≤ qcd ≤ programmed ≤ planned coming out of the tracker.
  • The Timeline dimension responds to real milestone status: any milestone past its date and not done should surface a red dot on the milestone track and fire the "Milestone overdue" signal.
  • daysToDBL derives from the live DBL milestone date, so the "DB lock at risk" signal and the Next-DB-lock KPI both move with CTMS.
Scope reminder. This monitor is the operational/program complement to TRIALMON_Dashboard.html: it watches deliverables, data readiness, TLF production, and milestones — not participant-level clinical signals. Participant-safety monitoring lives in TRIALMON. Keep that boundary when you wire sources: clinical signals belong there, operational metadata belongs here.

14Governance, guardrails & the two-dashboard split

This dashboard (Study_Lifecycle_Monitor.html) is built around a small set of non-negotiable guardrails. They are not decoration: they decide what the tool is allowed to show, where its numbers may come from, who is allowed to act on them, and why it sits in its own lane rather than overlapping the participant-safety dashboard. Read this section before you put any output in front of a lead, a sponsor, or an inspector.

The five non-negotiables

  1. Operations-only. The dashboard watches deliverables, data readiness, TLF production, milestones, and programming throughput. It never ingests PHI, never holds participant-level rows (no USUBJID/SUBJID records), and never displays reported or regulated clinical values.
  2. Reported numbers come only from validated tools. Any number that lands in a CSR, a TLF, or a submission is produced and signed off in a validated environment (for example Phoenix WinNonlin for NCA/PK, Pinnacle 21 for conformance, the EDC/CTMS of record). This dashboard restates operational metadata about those deliverables — counts, percentages, dates — never the regulated results themselves.
  3. Transparent scoring — no black box. Every risk score is a published arithmetic formula with stated thresholds. The exact dimension formulas and the RAG cut-points are printed inside the dashboard itself (the collapsible note under the heatmap) and in the README. There is no predictive model and no learned weighting to audit.
  4. Humans decide. The tool ranks, flags, and routes attention. It does not approve a database lock, accept a query backlog, or sign off a TLF. Every signal is a prompt for a person to look, not an action.
  5. Simulated demo data, clearly labeled. The shipped portfolio is seeded synthetic data, badged SIMULATED DEMO DATA in the header, footer, and README, and reproducible from the fixed seed. None of it is real study data.

Not validated software. The Study Lifecycle Monitor is decision-support, not a validated system. Do not treat any figure on it as the source of truth for a regulated deliverable, and do not cite it in a submission. When an operational number matters, confirm it against the system of record (CTMS, EDC, the TLF program tracker, and the relevant validated tools) before acting.

What "operations-only" rules out

The boundary is concrete. The following never cross into this dashboard, even via the live data adapter:

  • PHI or any directly or indirectly identifying participant information.
  • Participant-level records of any kind (one row per participant, per visit, or per event).
  • Reported clinical numbers — efficacy estimates, PK parameters, safety incidences, anything that would appear in a TLF or CSR result.
  • Unblinded treatment information.

What does cross is read-only operational metadata: study phase, milestone dates and statuses, enrolled/target counts, site counts, open-query counts and their weekly trend, clean and SDV percentages, days to DB lock, TLF stage counts, deliverable done/total tallies, and double-programming percentage with open-discrepancy count. These are program-management facts about the conduct of the work, not clinical observations from it.

A simple test for whether a field belongs here: if removing every participant from the study would leave the number undefined, it does not belong (that is participant-level data). An open-query count belongs; the content of any query does not. A TLF finalized count belongs; the values inside the TLF do not.

Transparent scoring: the formulas are on the wall

Because there is no model to trust, the scoring must be legible. Each study carries four dimension scores plus a weighted overall, all clamped to [0,100]. The collapsible note under the heatmap quotes them; they are reproduced here so you can reconcile any cell by hand.

Timeline = clamp( overdue×22 + dblPressure(s)×45 )
overdue = count of milestones with status risk; dblPressure: Startup = .15; otherwise by days-to-DB-lock d: d≤0 → 1, d<14 → .85, d<30 → .6, d<60 → .35, else .2

Data = clamp( ( min(1, openQ/120)×45 + (1−cleanPct)×30 + (1−sdvPct)×25 ) × (0.5 + 0.6×dblPressure(s)) )

TLF = clamp( (1 − finalized/planned)×100 × (0.3 + 0.8×repPressure(s)) )
repPressure: Reporting = 1, DB Lock = .6, Conduct = .25, else .1

Resource = clamp( (1 − dblProgPct/100)×55 + min(1, discrep/18)×45×repPressure(s) )

Overall = clamp( 0.30×Timeline + 0.28×Data + 0.27×TLF + 0.15×Resource )

RAG thresholds — rag(v): green if v≤33, amber (watch) if 33<v≤66, red (at risk) if v>66. Equivalently rag returns r when v>66, a when v>33, else g. clamp bounds every score to [0,100].

Two consequences are worth internalizing as a user. First, the overall weighting leans on Timeline and Data (0.30 + 0.28 = 58% of the score), so a study can turn red on schedule and data pressure even with TLF and Resource quiet — that is intended for a program lens. Second, both the Data and TLF dimensions are amplified by phase pressure, so the same raw open-query count or finalized fraction scores higher as a study approaches DB lock or enters Reporting. When a score moves and the raw inputs did not, check whether the study changed phase or its days-to-DB-lock crossed a pressure band.

The deliberate two-dashboard split

This is a conscious separation of concerns, not a gap. There are two dashboards and they do not overlap:

DashboardWatchesDoes not watch
Study_Lifecycle_Monitor.html (this one)Deliverables, data readiness, TLF production, milestones, programming throughput — the operational/program lifecycleAnything participant-level or clinical
TRIALMON_Dashboard.html (companion)Participant-safety monitoringProgram/operational deliverables

The README states the relationship plainly: this Study Lifecycle Monitor is the operational/program complement to TRIALMON. If you are asking "is the work on track to be delivered, locked, and reported?" you are on the right dashboard. If you are asking "is a participant or the population showing a safety signal?" that question belongs to TRIALMON, and nothing here is designed or governed to answer it.

Do not use operational health as a proxy for participant safety. A study can be green across all four operational dimensions and still have a safety concern that only TRIALMON would surface, and the reverse holds as well. Keeping the two lenses separate is what keeps each one honest and within its governed data boundary.

Deliberately excluded

The README lists what was kept out by design, and the footer repeats the core of it. This is standard, defensible operational monitoring only:

  • No predictive model and no experimental engine of any kind.
  • No research-work-in-progress or manuscript-specific methods — none of the author's personal-research constructs are present anywhere in this tool.
  • No participant-level data, no PHI, no reported clinical numbers.

The footer states it for the record: self-contained, offline · SIMULATED DEMO DATA (seeded, reproducible) — not real study data. Operational monitoring only: no PHI, no participant-level data, no reported clinical numbers; reported values always come from validated tools. Transparent operational scoring — no predictive model and no experimental components.

Going live without breaking the guardrails

When the synthetic generator is replaced by real feeds, the governance posture must survive the swap. Follow this sequence.

1. Implement loadPortfolio() to return the exact same study-object shape the generator produces, sourced read-only from the systems of record (CTMS, EDC, the TLF program tracker, the Protocol→CSR deliverables register) via REST or a nightly CSV export.

2. Map only operational metadata into the model — the per-field source list in the data adapter documents exactly which system feeds each field. Confirm no PHI, no participant-level row, and no reported clinical value is in the payload.

3. Remove the SIMULATED DEMO DATA badging only once live data is confirmed wired and validated against the source systems; until then, leave every honesty label in place.

4. Keep the scoring formulas visible. If weights or thresholds are ever tuned, update the on-screen collapsible note and the README in the same change, so the displayed math always equals the executed math.

5. Re-verify after any change: the embedded JS should pass node --check, and a headless render should produce no NaN and a full green/amber/red spread, exactly as the demo build is verified.

Even live, the rule does not move: reported and regulated numbers stay with the validated tools and the systems of record (for example Phoenix WinNonlin, Pinnacle 21, the EDC/CTMS of record). The dashboard's job is to point a person at the right study and the right deliverable fast — the person, working in the validated system, makes the call.