Workbench roles
A workbench role is the phase-4 layer over the ten personas. It is written
in full everywhere, in the contract, in code identifiers (workbench_role*) and in copy (“Working as:
Medicinal chemist”), and never shortened to “role”, because “role” is the RBAC role (roles,
role_assignments, org.roles.*, program.roles.*). A workbench role selects a home screen, a rail, a
toolset, skills, a prompt, a rubric and memory lenses. It never grants a permission. The fixed sentence
shown on the picker is:
Workbench roles change what you see first, never what you may do
Authorisation stays with the persona bundle held through role_assignments. The agent’s effective toolset in
a run is the intersection of three filters, none of which the workbench role widens: the role’s toolset, the
tools of the run role, and the allow-list computed from the person’s live permissions. Names are fixed in
PRODUCT-CONTRACT.md section 12.2 and decided in ADR 0027.
Every item on this page ships designed and moves to preview (on the
illustrative program first, then partner programs) only when the R4 gate list at the end of this page
passes. Nothing here runs for a customer today. Counts are R4 targets, re-derived on release by the owning
test.
The fifteen
WORKBENCH_ROLE_VERSION = 1, EXPECTED_WORKBENCH_ROLE_COUNT = 15. The catalogue lives in
api/app/auth/workbench_roles.py with a JSON export mirrored by web/lib/workbench-roles.ts and by
WORKBENCH_ROLES in docs/lib/content.ts; two sync tests read the export. Record types are permission
resources <product>.<resource> whose .manage key exists and is held by at least one mapped persona at
persona version 3, so a proposal from a role can always be decided by someone who wears it. Where a person
holds several personas, the mapped bundle is the union; sections of the role home appear only for personas the
person holds.
| Key | Name | What they decide | What they see first | Record types (proposals land here) | Persona | Harness at R4 |
|---|---|---|---|---|---|---|
medicinal_chemist | Medicinal chemist | which compounds to make next; series progression; which SAR hypotheses to test | SAR tables by series, property profiles, notebook synthesis entries, assay results | evidence.hypotheses, evidence.findings, evidence.questions, plans.experiments | Scientist | own prompt; preview candidate |
biologist_pharmacologist | Biologist / pharmacologist | target validation calls; assay readiness and acceptance; which model answers which question | target dossier, assay protocols, pathway context, dose-response figures | evidence.questions, evidence.findings, plans.protocols, plans.experiments | Scientist | neutral scientist |
dmpk_tox_scientist | DMPK and toxicology scientist (bioanalytical and safety pharmacology folded in) | dose-range and GLP study proposals; NOAEL interpretation as proposals; species rationale; FIH inputs; bioanalytical method suitability | PK parameter tables, exposure margins, tox reports, read-across context, guidance text | evidence.findings, evidence.gaps, plans.protocols, plans.reports | Scientist | own prompt; preview candidate |
cmc_process_scientist | CMC / process scientist | route, salt and form, batch strategy, specification proposals, tech transfer, supply for tox and FIH | batch records, impurity and stability tables, quality guidelines, supply dates | plans.plans (both personas); progress.milestones, progress.dependencies (manufacturing specialist); plans.protocols, evidence.findings (scientist) | Manufacturing specialist Scientist | neutral of the persona held |
clinical_pharmacologist | Clinical pharmacologist / translational lead (pharmacometrics and clinical operations folded in) | FIH dose and escalation rationale; PK/PD plan; phase 1 design inputs | NCA parameters, projections with inputs shown, label precedents, registry precedents | evidence.findings, evidence.hypotheses, plans.protocols, plans.reports | Scientist | neutral scientist |
regulatory_strategist | Regulatory strategist | what to claim; agency questions; the pre-IND meeting package; which guidance governs | guidance changes with version, precedent labels and trials, every open agency question | evidence.gaps, evidence.sources, evidence.questions, plans.reports; requirement sets are a hand-off to qa_reviewer | Scientist | neutral scientist |
medical_writer | Medical writer | module structure; citation completeness; what is missing before filing | the outline with sources, every source behind every sentence | plans.reports, evidence.sources | Scientist | neutral scientist |
qa_reviewer | Quality reviewer (QA) | check outcomes, deviation classification, requirement-set acceptance; twin and memory oversight | SOP text beside the record checked, version diffs, checks due, deviations | review.sops (checks, requirement sets), review.findings, review.deviations | Quality reviewer | neutral quality_reviewer |
program_manager | Program manager | plan approvals, open items, milestone changes, hand-off routing | critical path, decisions waiting on others, blockers, approvals older than a set number of days | progress.milestones, progress.dependencies, progress.open_items, progress.approvals, plans.plans, program.handoffs | Program lead | neutral program_lead |
analytical_qc_scientist | Analytical / QC scientist | method suitability, specifications, stability design, OOS and OOT candidate classification | method and stability designs, results and OOS candidates | plans.protocols, plans.reports, evidence.findings (oos_candidate finding kind; hand-off to QA) | Scientist | neutral scientist; skills R5 |
formulation_scientist | Formulation scientist | tox and FIH formulations, excipient compatibility, solubility enhancement, dose-form selection | formulation trials and studies | plans.protocols, plans.experiments, evidence.findings; progress.dependencies (manufacturing specialist) | Scientist Manufacturing specialist | neutral of the persona held; R5 |
computational_biologist | Computational biologist | genetics, expression, structure and pathway calls | target hypotheses, datasets and sources | evidence.findings, evidence.hypotheses, evidence.sources | Scientist | neutral scientist; R5 |
biostatistician | Biostatistician | design adequacy, analysis plans, dataset conformance readiness | designs and analysis plans, statistical reports | plans.protocols, plans.reports, evidence.findings | Scientist | neutral scientist; R5 |
ip_analyst | IP analyst (never legal advice) | landscape completeness and which families go to counsel | patent documents, landscape gaps | evidence.sources, evidence.findings, evidence.gaps | Scientist | neutral scientist; tools R6 |
portfolio_head | Portfolio head | the go/no-go chair; a decider on evidence | go/no-go decisions, program milestones | progress.approvals; stage gates (R5) | Program owner | neutral program_owner; R5 |
Two facts shape the QA and regulatory rows. Checks and requirement sets have no .manage key of their own:
both are written under review.sops.manage, so the QA record types are spelled review.sops,
review.findings and review.deviations, and “checks” and “requirement sets” are display groupings. The
scientist does not hold review.sops.manage, so a regulatory strategist’s requirement set is a hand-off to
the quality reviewer, never a proposal in the strategist’s own name.
Folded rather than added: the bioanalytical scientist and the safety pharmacologist into dmpk_tox_scientist
(with the skills dmpk.bioanalytical_method_checklist and tox.pk_parameter_extraction), the
pharmacometrician and clinical operations into clinical_pharmacologist. Roadmap, not keys: clinical
operations lead, pharmacovigilance physician, data manager, alliance / BD. No workbench-role key equals a
persona key (qa_reviewer, not quality_reviewer) and no key is a substring of any permission string.
What a role changes, and what it never changes
| Changes with the workbench role | Never changes |
|---|---|
the role home (/p/[slug]/home): four regions in fixed order, “Today’s decisions”, “My open questions”, “My evidence board”, “My program in this role’s terms” | the permission bundle: PERSONA_VERSION stays 3 and no permission key is added in R4 |
| the rail: the role’s products promoted, the rest under “All records”; Setup and Organization stay under “More” by permission (the program manager promotes Setup) | the records: products stay the record of truth, the H1 keeps the contract record name and the role label is the subtitle |
section labels from words.workbenchRoleLabels[<role_key>], for example “SAR hypotheses” for the chemist’s hypotheses or “Study designs” for the DMPK scientist’s protocols | the approval matrix: who decides what is read from the personas alone |
| the harness the agent wears in a run: prompt, toolset, skills, rubric, memory lenses (harnesses) | the assertion model, the proposal gate, the licence rules and the audit envelope |
which duty templates a twin may run: R4 ships sar_refresh (medicinal chemist) and study_gate_check (DMPK and toxicology scientist); guidance_watch, stability_pull_reminder and shortage_watch are R5 and designed, held in the agent’s RESERVED_ROLE_TEMPLATES until their tools enter the catalogue (verified in agent/trovensa_agent/twins/duties.py:79-83); blocker_scan, pre_read and the QA four are persona templates that run whether or not the person wears the workbench role | the twin’s authority for duties: twin runs keep persona_key |
Every proposal made under a workbench role reads “Proposed by <Name>’s twin (agent, working as <workbench role>); decided by <Name>”, with the role name in the person’s words (“working as Medicinal chemist”). A run
under a neutral persona harness reads “working as <persona name>”.
Choosing, assigning, falling back
- Choosing (self): the picker on the program home, an inline segmented list or listbox in the header
limited to the roles whose persona tuple intersects your personas, calls
PATCH /me/settingswith{workbench_role: {<program_id>: <role_key>}}. The value is stored per person and program inusers.settings.workbench_role[program_id](USER_SETTING_WORKBENCH_ROLE, the eighthUSER_SETTING_*constant). A key you may not wear is refused422 workbench_role_not_heldand the error sits beside the picker. Auditedworkbench_role.set. See How to choose your workbench role. - Assigning (by a lead):
PUT /programs/{program_id}/workbench-roles/{user_id}with{workbench_role_key, reason}underprogram.members.managewrites the member’s setting; the member’s header reads “Set by<Name>on<date>. Change” and audit recordsworkbench_role.assignwith the lead as actor and the member as target. On Setup › Members, “Assign workbench role” expands inside the member’s row. - Fallback when nothing is set: the agent wears the neutral persona harness (never a workbench role
key): scientist ->
scientist, quality reviewer ->quality_reviewer, program lead ->program_lead, program owner ->program_owner, manufacturing specialist ->manufacturing_specialist, viewer -> no harness (read-only console). With several personas the most senior wins in the fixed order owner > lead > quality reviewer > manufacturing specialist > scientist. The fallback never writes the setting: the home keeps showing “Choose your workbench role” until you or a lead sets it, and it is audited once per person and program asworkbench_role.default_applied. The picker’s suggested first choice per persona (qa_reviewer,program_manager,portfolio_head,cmc_process_scientist) is copy, not a stored value. - Delegation: when a delegate acts for someone, the run acts as the delegate, the harness is the
delegate’s, the memory read is the delegate’s own, the principal’s memory is never read or written, and
proposed notes land in the delegate’s own memory tagged
on_behalf_of. The inbox keeps “Deciding for<Name>” and “Routed under delegation from<Name>; decided by<Name>in their own right.” - Switching a workbench role mid-conversation never switches a running harness; a harness never changes inside a run.
Twin schedules shown beside a role stay the twin design’s illustrative defaults and are labelled so wherever they appear.
Coverage tests
Five tests in the API’s make check keep the layer honest: test_workbench_roles_cover_personas;
test_workbench_roles_never_in_permissions (no key a substring of a permission string; no key equal to a
persona key); test_record_types_are_permission_resources (every record type’s .manage key exists and is
held by a mapped persona at persona version 3); test_harness_toolset_subset (every harness toolset key is
in the tool catalogue, its permissions are a subset of the mapped personas’ bundles, and no untrusted-text
tool sits in any toolset); test_workbench_role_labels (every label in words.workbenchRoleLabels differs
from every contract record name and persona name and carries no vendor word). docs/lib/content.ts repeats
the first two at typecheck time. The reader is not a workbench role; every harness delegates retrieval to the
reader sub-agent.
Routes, DTOs and audit actions
| Route | Rule | Returns |
|---|---|---|
GET /workbench-roles | authenticated | WorkbenchRoleOut[] {key, name, personas, record_types, rail, harness_key, status, label_map_key, version: 1} |
GET /programs/{program_id}/workbench | program.settings.read | WorkbenchOut {workbench_role_key, name, decisions, other_decision_count, questions, pins, status_lines[{text, record_ref, as_of}], sources[{provider, status, verified_on?}], twin, memory_summary (at most 3), set_by?}; RecordRef {record_type, record_id, href} |
PUT /programs/{program_id}/workbench-roles/{user_id} | program.members.manage | WorkbenchRoleAssign {workbench_role_key, reason} -> WorkbenchRoleAssignmentOut {user_id, program_id, workbench_role_key, set_by_user_id, set_at, reason} |
PATCH /me/settings | authenticated | UserSettingsUpdate.workbench_role?: Record<program_id, WorkbenchRoleKey | null>; null clears the program’s entry, audited workbench_role.set with new: null, and the role home returns to the persona home (contract 12.3, 2026-09-16) |
GET /me | authenticated | MeOut.user carries settings: UserSettings (workbench_role included), so the web reads the workbench role from the identity call (contract 12.8) |
Fact (verified in api/app/schemas/console.py:269, api/app/routers/me_settings.py:50-57,
api/app/schemas/identity.py:70-74 and web/components/workbench/role-picker.tsx:128-131): the settings schema
types the map as dict[uuid.UUID, str | None], the router pops a program’s entry for null, MeOut.user carries
settings: UserSettings, and the picker offers Use the persona home whenever a role is set | Consequence:
choosing, clearing and reading the role are built; the feature as a whole keeps the status word designed until
the R4 gates pass.
Audit actions: workbench_role.set, workbench_role.assign, workbench_role.default_applied. Error code:
workbench_role_not_held (422). Source statuses on the home: connected, not_connected, degraded;
terms_changed (a weekly probe hashing each provider’s terms page) is R5 and designed (verified in
api/app/db/models/connection.py:16: the status does not exist and rate_budgets has no terms columns).
The R4 gate list
Workbench roles, the evidence panel, skills and memory tiers move from designed to preview together, and
only when every item below is green (contract 12.1, Part 9 section 8):
checkandverifygreen on every repository;permissions in sync: 87;EXPECTED_TABLE_COUNT = 63;EXPECTED_ROUTE_COUNT = 206;EXPECTED_HARNESS_COUNT = 20;EXPECTED_WORKBENCH_ROLE_COUNT = 15.- The renderer-per-licence tests and the “abstract body withheld when the abstract’s own licence code is publisher copyright” test.
- The two-organisation and two-program memory isolation tests; the injection fixture through every provider
handler and through
memory.propose_note. - The container tests: every flagged untrusted tool absent from every non-reader agent,
documents.list_assetspresent,harness_deniedhandled, refusal lines present. - The skill golden tests and
skill-references-check(5/13 references verified). no-popups.lint.test.tsand the plumbing-word copy test inside the web app’snpm run check(recorded 2026-09-16; today they run undernpm run test, whichverifycalls).- The screenshot set, taken by
npm run shots:workbenchagainst the running web and API, with zero browser console errors at 1440 and 390: two role homes; one CC BY article with a figure and its attribution line (PMC10000000.1); one no-derivatives source as a quote in context; one publisher-copyright source as metadata and link; one notebook entry; one rendered PDF page. - A live twin cycle under a harness on the illustrative program.
Reserved for later releases and never presented as live: the remaining harness prompts, lens reviews, tier 3
memory curation, stage gates and PERSONA_VERSION 4 (R5); ip_analyst tools, per-user connector grants,
PK/PD simulation and organisation twins (R6).
Related
- How to choose your workbench role.
- Harnesses, Skills, Memory, Evidence panel.
- Personas and About personas and twins.
Source: PRODUCT-CONTRACT.md sections 12.1, 12.2, 12.3, 12.7, 12.8 and 12.11; PRODUCT-PLAN.md Part 9 sections 2 and 3 and Part 8; api/app/auth/permissions.py (EXPECTED_PERMISSION_COUNT = 87; checks and requirement sets written under review.sops.manage); api/app/auth/personas.py (PERSONA_VERSION = 3); api/app/db/models/org.py and twin.py (the seven USER_SETTING_* constants before USER_SETTING_WORKBENCH_ROLE); forge.config.json (words.workbenchRoleLabels, words.plumbing); docs/lib/content.ts (WORKBENCH_ROLES, R4_TARGETS); decisions/0027-workbench-roles-as-ux-and-harness-layer.md; decisions/0031-harness-from-the-run-row.md