About programs
An Organization is the tenant: one identity-provider tenant, its users, groups and role assignments.
A Program is the scoped workspace of one drug-development program and the only scope kind besides the
organisation. Every scoped record carries organization_id, program_id and stage_id, and every
program route lives under /api/v1/programs/{program_id}/...; in the web app the tree is
/p/<slug>/....
The problem
Teams work inside one development program at a time, and a program has its own milestones, dependencies, approvals, connections and members. A generic “space” would put two words on one idea, and a product called “Programs” inside a program would collide with the scope. Permissions had to separate authors from approvers without a second model of roles.
How it works
- Every new organisation gets one program with slug
default(“Default program”, statusactive, stagediscovery). The illustrative seed addstrv-101(“Illustrative program TRV-101”). - A role assignment binds a user or a group to a persona with
scope_kindorgorprogram(with aprogram_id). Effective permissions are the union of direct and group assignments; there are no deny rules. - Organisation personas (
org_owner,org_admin,org_member,org_auditor) hold onlyorg.*keys and apply everywhere in the organisation. Program personas (program_owner,program_lead,scientist,quality_reviewer,manufacturing_specialist,viewer) hold program-scope keys and apply inside one program. /mereturns the organisation, the org permissions and, for every program the person can open, its slug, name, current stage, status and permissions. The web rail and panel show only what those permissions allow; there are no role-name checks in the UI.- Connections, agent runs and the audit log are per program (
Setup › Connections,Setup › Agent runs,Setup › Audit); organisation-wide audit isOrganization › Audit.
Why this design
- Two scopes and not three: the brief never needed a grouping above the program or below it; fewer scopes keep every query to three filter columns.
- Personas rather than free-form roles: ten versioned bundles seeded by migration
0001are auditable and re-seeded idempotently at API start-up; a custom role can still be created from the same catalog. - Separate
.approvepermissions:plans.protocols.approve,plans.reports.approve,evidence.hypotheses.approve,review.findings.approveandprogress.approvals.approvelet ascientistdraft and propose while approvals stay with approvers. manage_ownersas its own verb:org.roles.manage_ownersandprogram.roles.manage_ownersare the only difference between an owner and an admin or lead, and the API guards owner grants and refuses to remove the last organisation owner.
Consequences and limits
- Moving a record between programs is not a supported operation; records belong to the program they were created in.
- A person with organisation permissions but no program persona sees the organisation pages and no program.
- The program slug (
^[a-z0-9][a-z0-9-]{0,63}$) is part of every web URL, so renaming a slug changes links.