Skip to content
Browse docs

Supply Plans

How supply plans stay trial-scoped, preserve blinding, and retain governed history from authoring through forecasting.

Why it matters

A supply plan belongs to exactly one trial and lives under Trial Build Supply beside Budget. It combines the trial’s products, demand rules, and supply policy into one governed record, then forecasts demand and feasibility from retained inputs. Three guarantees make that record acceptable for regulated planning:

  • Trial-scoped access — plans and their connections never cross organization boundaries, and destructive transitions stay admin-gated.
  • Blinding preservation — blinded packs carry display codes, treatment mappings stay in a separate authorized source, and the forecast engine never consumes blinding state.
  • Data retention — versions are immutable, history stays readable after retirement, and permanent delete is disabled.

Trial-scoped access

flowchart LR
    Org[Organization boundary] --> Trial[Trial]
    Trial --> Plan[Supply plan]
    Plan --> Sites[Site connections]
    Member[Org member] --> Author[Author and configure]
    Admin[Org admin] --> Retire[Retire and restore]

Every supply plan, site connection, baseline selection, and forecast run is owned by exactly one organization and exactly one trial. Repositories filter by organization on every read and write, so a plan created in one organization is unreachable from another — there is no sharing affordance to misconfigure. Plans are created from their owning trial, the owning trial can never be changed or disconnected, and a trial can hold multiple plans with the selected plan kept in the URL.

Within the organization, capabilities split two ways:

ActionOrg memberOrg admin
Create, edit, configure, compare, forecast, replayYesYes
Retire (soft delete) and restore a planNoYes
View retired recordsNoYes

The owning trial is validated inside the caller’s organization at creation: the trial must exist there. Plans are created from the layout header action; the single trial contribution (coverage, demand model, source review) is configured from the plan header actions and can never be moved or disconnected.

Connections

Plans keep the owning trial contribution (coverage, demand model, retained source pins) on the plan itself and connect to sites and interventions through governed junctions, and link media evidence through the shared entity-media surface. Each junction is organization-scoped, versioned, and soft-deleted with reason and retain-until metadata. Site and intervention links must belong to the owning trial; the server rejects cross-trial links.

  • Sites carry the planning membership used by manifests and replay, alongside the owning trial contribution.
  • Interventions link dispensable products to the plan without moving treatment mappings into plan content.
  • Media attaches forecast reports, distribution plans, and supply agreements with domain purposes.

Organization, contact, document, and computerized-system links stay parked: they ship no tabs and no API surface. Baselines name the governing plan version per requirement scope and period, with overlap rejected at write time. Cloning stays in the owning trial and copies the reusable definition with fresh internal identities while leaving trial-bound rows and connections behind.

Blinding preservation

Each finished pack declares its blinding explicitly:

  • open packs dispense under their own code.
  • blinded packs dispense under a display code. The mapping from display code back to treatment lives in a separate authorized source referenced by the pack — never inside the plan content.

Readiness enforces the authoring contract before any forecast runs: a blinded pack without a display code fails the blinding gate and blocks execution, while a blinded pack with no retained mapping is reported as an informational finding for review.

The forecast engine itself never reads blinding state. Demand compilation, inventory ledgers, and feasibility math operate on internal pack identities only, so execution cannot unblind through computation — there is no mapping in the engine to leak. Forecast outputs therefore carry internal pack identities; blinded display remains a dispensing and labelling concern owned by the pack definition.

Data retention

flowchart LR
    Save[Governed save] --> Version[Immutable version]
    Version --> History[Readable history]
    Retire[Admin retire] --> Readable[Still readable]
    Readable --> Restore[Admin restore]

Every save creates a new immutable version; history is never rewritten. Restoring an earlier version appends a new version with the restored content, and every business-changing write carries the actor plus the change rationale into the audit trail.

Retiring a plan, connection, or baseline selection is a soft delete: it records the reason and an optional retain-until date, stays readable in history, and excludes the record from live planning. Only organization admins can retire or restore, and retired records require an admin role even to list. Permanent delete endpoints exist only to refuse: they answer 403 under the compliance retention policy.

Retained evidence compounds per version rather than being overwritten:

  • The first plan version pins its newly created owner-trial contribution. Each plan version pins a membership manifest — the exact trial and site versions it was saved against, with content checksums.
  • Each forecast run retains its input snapshot and publishes validated results inline, so a completed run stays interpretable after the live records move on.
  • Replay runs resolve the pinned versions from the manifest instead of live records, which is what makes a historical recomputation trustworthy rather than a fresh forecast wearing an old name.
  • Baselines name the governing plan version per requirement scope and period, with overlap rejected at write time, so the record of what governed when is itself governed.

Forecast table

Forecast jobs are dispatched after the forecast, audit record, and linked workflow are committed. Readiness and execution inspect all site links, including later pages; invalid mappings block calculation.

The Forecast section shows one demand table instead of separate forecast, material, and implementation views. It is anchored to a specific plan version with the newest run selected, and stays useful before any calculation: rows read as not calculated until results exist.

The table lists expected packs and dispensing events per day, pack, location, and requirement, with shortage causes inline. Run history, exact run selection, settings, findings, scope, baselines, and comparison live in the details drawer; recalculation requires saved plan edits first. There is no single green badge: a finished forecast can coexist with unreviewed mappings.

Selecting a run calculated from an older plan version raises a staleness notice: the historical result stays available with its original inputs, and recalculation evaluates the revised plan. The Export action downloads the retained forecast evidence; recipient-specific handover packages remain planned output choices.

What to watch for

  • A plan version whose connections changed since it was saved fails the reproducibility gate. Save a new plan version to refresh the manifest — the old version stays intact as evidence.
  • Cloning a plan stays in the source plan’s owning trial and copies the reusable definition with fresh internal identities. Trial-bound rows (dispensing rules, eligibility rules, allocation rules, trial-scoped service targets) and connections do not transfer, because they reference junctions of the source plan.
  • Forecast runs stay in not_run for inventory and capacity until real inputs exist; an unevaluated model never hides behind a green badge.