Find Hours, Budget Reconciliation (effort only)

A visual, runnable guide, from the biostatistics lens

Run / share / skim / file / build, pick your mode. This page shows the reconciliation (diagrams + a live roll-up computed from the ledger), the runnable R Shiny app lets you run one interactively, the dashboard is a shareable snapshot, the one-pager is the skim, the Excel workbook is what you file, and the build-it walkthrough shows how the app is made. Operations-only, synthetic, hours / effort only (no dollars, rates, or headcount). We say participant, not subject.

The whole idea, in one line

When a new out-of-scope ask lands, you do not immediately ask for more. First you find the hours you already have: honest under-run slack across the tasks that are tracking under plan, plus the management reserve. Only the residual becomes a change order.

\[\textbf{Net findable} \;=\; \big(\textstyle\sum \text{Planned} \;+\; \text{Contingency}\big) \;-\; \textstyle\sum \text{EAC}\]

Under-run slack is the part of your planned effort you are forecast not to spend (planned minus estimate-at-completion, floored at zero). Contingency is the management reserve. Add them and you have the effort you can redirect before touching the contract.

What to do with a new ask, the decision tree

Yes

Partly / No

Yes

Partly / No

Yes

No

New ask,
estimate effort

Slack
covers it?

Reallocate
under-run slack

Contingency
covers rest?

Draw reserve,
log it

Defer /
re-sequence?

Free planned
hours

Change order,
the residual

Absorbed
internally

The tool automates the first two branches: it sources hours from under-run slack first, then contingency, and reports whatever is left as the change-order residual. Deferring / re-sequencing is a human decision you make before you accept a residual.

Where every number comes from, the source map

The ledger you see below is a picture built from synthetic data. On a real study each column is fed from a system of record, and the tool never invents a number.

Planned

Actual

% done, EAC

SOW / budget
grid, CTMS

Effort
ledger

Timesheet
system

Estimate +
progress

Roll-up,
net findable

Change-order
process

Column Source of record
Planned SOW / budget grid in CTMS
Actual to date Timesheet system
% complete, EAC Your estimate + the progress tracker
Change order Contract process: lead biostatistician + PM review, sponsor sign-off, SOW amended

The tool does not connect to CTMS or timesheets. You transcribe current figures into the ledger, on purpose, so the reconciliation is attributable and stands on its own. Every number reported here comes from the validated ledger and the shared logic core, never from an assistant.

The live roll-up, computed from the ledger

This is the same logic the Shiny app and the Excel workbook use, rendered here from the effort ledger, project_hours_ledger.csv (in the templates/ folder). Edit that ledger (or run the app) and these move.

850
Planned (h)
513
Spent to date (h)
831
Forecast EAC (h)
40
Contingency (h)
59
Net findable (h)

Net findable here is 19 h of under-run slack (planned 850 − EAC 831) plus 40 h of contingency = 59 h you can redirect before a change order.

Tasks where the blue (planned) bar sits above the gold (forecast) bar are the ones giving up slack. Where gold exceeds blue, that task is over-running and is being watched, not raided.

Deliverable Planned Actual % done EAC Under-run slack
SAP + randomization schedule 80 78 100 78 2
ADaM specifications 60 55 100 55 5
ADaM programming 120 90 70 128 0
ADaM QC / validation 80 40 60 70 10
TLF programming 200 120 60 210 0
TLF QC (double-programming) 120 50 45 110 10
PK / NCA (Phoenix) 60 30 55 55 5
Interim / DSMB outputs 40 40 100 40 0
CSR tables + narrative support 90 10 10 85 5

Under-run slack is per-task max(0, planned − EAC). It sums to the portfolio net slack of 19 h.

Testing an ask, two worked examples

Ask: 45 h → Absorbable internally
19 h  from under-run slack
26 h  from contingency reserve
0 h  residual (change order)
Contingency left after this ask: 14 h.

A 45 h ask is fully covered: 19 h from under-run slack and 26 h from contingency, no residual. It is absorbable internally with 14 h of reserve still held back.

Ask: 90 h → Change order needed: 31 h
19 h  from under-run slack
40 h  from contingency reserve
31 h  residual (change order)
Contingency left after this ask: 0 h.

A 90 h ask exhausts the 19 h of slack and the full 40 h of contingency, leaving a 31 h residual. That residual is genuine new scope: it goes to the change-order process (lead biostat + PM review, sponsor sign-off, SOW amended). The tool drafts the hours-only note; pricing is handled downstream by the PM.

Run it, and build it

The interactive tool is a real R Shiny app you own. From the package root:

shiny::runApp("budget_reconciliation/shiny")
# needs: install.packages(c("shiny","bslib","DT","ggplot2"))

Edit a task’s EAC, change the ask, and the net-findable roll-up and verdict recompute live. Download the reconciled ledger to CSV to drop into the Excel workbook.

You want to build these yourself. The whole app is small: shiny/R/logic.R holds the reconciliation math (the functions this page also called), and shiny/app.R is the UI + reactivity. The build-it walkthrough takes you through it piece by piece. Change the ledger CSV to your study and it is your reconciliation.

The other formats

Mode Artifact
Run Run & build the Shiny app, edit EAC, test an ask, download
Share (no R) Dashboard, the roll-up as a self-contained page
Present Slides
Skim One-page cheat-sheet (PDF)
File Excel workbook, the record you keep
Build Build-it walkthrough

Synthetic data throughout. Hours / effort only. A change order is a named-human, contracted decision; pricing is handled downstream by the PM / project finance.