Complete single-file export — user documentation and technical documentation, with all screenshots embedded. No internet connection and no other files required.
src/web/public/docs/ by
npm run docs:bundle. The browsable version lives at
/docs/index.html in the running application.
User documentation
Why AssayLink exists, where it sits in the assay landscape, and how scientists, stewards and project teams use it.
AssayLink is an AI-assisted platform for discovering, authoring, governing and reviewing reusable assay definitions in Pharma R&D. It is the intended successor to the Test Definition Portal (TDP): the place where an assay is defined once, in governed language, so later systems can capture and analyse results against that definition.
Today definitions live as ad-hoc JSON, Excel and ageing tools. Metadata is missing or unstandardised, finding a relevant assay is slow, and there is little room for automation. A new portal by itself does not fix that. Stewardship and a controlled vocabulary are what make data consistent across labs and interoperable across PDE, Helix, RedLIMS and reporting systems.
When a project needs “a CYP3A4 induction assay in human hepatocytes”, AssayLink should already hold an approved, findable definition — not another spreadsheet rewrite of the same assay.
Three things are first-class:
AssayLink governs reusable assay definitions. It does not store experimental execution data, raw instrument files, or processed results, and it does not replace LIMS, ELN, PDE or Benchling. Those systems remain the systems of record for work that was actually run; they validate entered data against AssayLink templates.
| AssayLink owns | AssayLink does not own |
|---|---|
| Reusable assay definitions (templates) | Experimental execution data (runs, plates, samples) |
| Template versions and review workflow | Instrument files, LIMS results, study reports |
| The assay metadata model itself (sections, fields, rules) | Numeric experimental values (Fold Induction, Ct, peak area, …) |
| Controlled vocabularies and stewardship | Protocol / instruments / data-output blocks (future sections); project master data (MaRLay) |
| Persona | Demo user | Main job |
|---|---|---|
| Assay Definition Owner | Dr Amelia Ostrowski | Create and edit draft templates; submit for review; version approved work |
| Data Steward | Dr Sofia Lindqvist | Review templates; own the metadata model and vocabulary |
| Project Team Member | Tomas Berg | Pick a MaRLay project, describe a need, link a matching template |
In this proof of concept, authentication is mocked. The header shows a Role control with the role name (Assay Definition Owner, Data Steward, Project Team Member). Hover to see the demo user. Changing the dropdown switches immediately and lands on that role’s dashboard. In a real deployment this comes from Entra ID / ALB SSO and cannot be changed in the UI.
User documentation
Screenshot walkthrough of every major screen, organised by persona. Switch the header persona to match the section you are reading.
Every page shares the same header: the AssayLink logo (home), a role-filtered navigation on one row, and (in the POC) the Role switcher on the right. The dropdown shows the role; hover for the demo user’s name. Changing it switches immediately. Links you are not allowed to use simply do not appear — and typing a forbidden URL shows a clear “not permitted” page.
Demo user: Dr Amelia Ostrowski. Creates and maintains templates through review.
Three columns: My draft templates, Awaiting steward review, and Recently approved. Drafts offer an Edit shortcut.
Templates are grouped into functional areas (ADME / DMPK is populated; HTS, Safety, In Vivo tiles are greyed placeholders for future scope). Open a group to see templates; filters sit at the top.
Shows metadata, status, versions, review history, the Metadata Quality Score, and actions (submit, edit draft when allowed). Vocabulary-backed values render as governed terms. AssayLink does not show experimental result values.
A metadata-driven wizard paged by steward-configured sections
(Assay Identity, Classification, Biological System / Materials, Detection,
Readouts / Endpoints, …). Fields, obligation and dropdown values come from
MetadataFieldDefinition; conditional rules show or require extra
fields (for CYP induction: Target, Cell System, Species, Detection Type).
Expected readouts are vocabulary categories, not measured values. A
Metadata Quality Score updates as you fill. Hierarchy fields
use the vocabulary tree; AI autofill (when enabled) is visibly marked as
suggested until accepted.
Owners can reopen drafts and “changes requested” versions. The template code is locked; metadata remains editable until submission.
Describe an assay need in free text. The system extracts metadata (rule-based, optionally merged with an LLM), scores published templates, explains each match field by field, and names fields that active metadata rules still require. A Metadata Quality Score estimate is shown from the description — not from experimental results.
Demo user: Dr Sofia Lindqvist. Reviews templates and owns the semantic foundation (model + vocabulary).
Lists submitted / in-review templates with the Metadata Quality Score (missing required fields, ungoverned values, rule warnings). Open a template to approve, reject, or request changes with comments and optional per-field notes.
A field is one piece of information an assay definition captures — “Assay format”, “Species”, “Target”. Fields sit in steward-named sections (including Biological System / Materials). This screen is what the authoring wizard is generated from.
The knowledge model shows Assay Definition → sections → vocabularies → rules → quality. Section cards sit below. The field and vocabulary map shows scientific sections and fields on the left, vocabularies on the right, connectors for governed values. Conditional rules live under the Rules tab and the Conditional Rule Map.
Adding a field. Inputs marked * are required. The two
settings that decide how the field behaves are:
TERM means values must come from a governed
vocabulary, so a vocabulary must be selected and the input type must be a
SELECT, MULTISELECT or HIERARCHY_SELECT.
Anything else (STRING, NUMBER, …) is entered freely.
Retiring a field. Deprecate is the governed route: authors stop being offered the field, existing template values are kept so history stays readable, and the rules become read-only until the field is reactivated. Delete is only offered when no template has ever held a value for the field — it removes a mistyped or experimental field outright, and the audit entry survives the deletion.
Leave Applies to assay types unticked and the field applies to every assay type. Tick a branch and it only appears once an author selects that assay type or one beneath it — which is why a newly scoped field can look missing in the wizard until the assay type is chosen.
Conditional rules. Under the Rules tab, a steward writes when/then logic over field keys. The CYP induction rule is the demo: when Assay Type is CYP induction or a more specific type, require Target, Cell System, Species and Detection Type, and restrict Expected Readout to Fold Induction, Emax and NOEL. Hide / make-optional stay secondary. Rules never collect experimental numbers.
Browse vocabularies, open a vocabulary to manage terms, synonyms, hierarchy and ontology mappings. Deprecate carefully; never delete history that templates rely on without a steward decision.
AI and users may propose labels that are not yet governed. Only a steward can approve them into an active vocabulary term. This is the single gate for growing the terminology.
Demo user: Tomas Berg. Starts from a MaRLay project (six-digit id), not from a blank matcher.
Open a project to see linked assays. “Define” starts a need description (high-level type + short text), runs matching pinned to that project, then lets you link a template version and add project-specific context.
All personas can browse the catalogue and open published templates. Creating, reviewing, and model edits remain role-gated.
If you open a URL your role cannot use, AssayLink shows a clear forbidden page naming the capability and which role holds it.
Technical documentation
For developers and coding agents: what AssayLink is, what is already built, and how to change it without breaking the product rules.
Goal: a POC that can mature into a product — the assay definition layer (intended TDP successor): AI-assisted template discovery, metadata authoring, vocabulary governance and template review. Not experimental execution data. Stewardship and controlled vocabulary are first-class, not an afterthought.
Two rules that override everything else:
Canonical project instructions also live in the repo root
CLAUDE.md. Prefer this HTML pack for humans and offline
export; keep CLAUDE.md as the short agent policy file.
| Capability | Status |
|---|---|
Next.js 15 App Router app under src/web/ | Done |
| Prisma + PostgreSQL schema, migrations, seed from 4 JSON sources | Done |
| Three personas with capability-based auth (mock cookie) | Done |
| Grouped catalogue, template detail, lifecycle statuses | Done |
| Metadata-driven authoring wizard (by section) + draft edit | Done |
| Steward review queue + review decision form | Done |
| Vocabulary CRUD, suggestions gate, change log | Done |
| Metadata model: Assay Definition Knowledge Model (sections, field–vocabulary map, rules) | Done |
| Metadata Quality Score (completeness, vocabulary, rules) | Done |
| Free-text matcher with explained scoring and rule-derived gaps | Done |
| Hybrid extraction (rules + optional Claude / Azure OpenAI) | Done (mock default) |
| Project-first flow via MaRLay mock + ProjectAssayLink | Done |
Static HTML docs at /docs/ | Done |
| Real Entra/ALB SSO | Designed, not wired |
| Real MaRLay HTTP client | Mock client shaped for swap |
| Reusable assay subtypes as a primary object | Out of primary UX (ADR-0003) |
| Protocol / instruments / data-output blocks | FUTURE sections on the section map |
app/ → server/services/ →
server/repositories/ → server/db.ts.
Components never call Prisma.
src/domain/, framework-free, with Zod.
src/server/ai/, Zod-validated,
cannot activate vocabulary terms or approve templates.
src/server/auth/
(guardCapability / requireCapability).
src/web/ — nothing the Docker
image needs may live above that folder.
npm run lint,
npm run typecheck, npm test in src/web.
ph-rnd-assaylink/
├── CLAUDE.md # Agent/project policy (short)
├── config.yml # ONLY infra file to edit
├── terraform/ # DO NOT MODIFY (upstream)
├── docs/ # Markdown ADRs & product notes (source material)
├── .github/workflows/ci.yml # Our CI
├── docker-compose.yml # Local Postgres
├── dev.sh # One-command local start
└── src/web/ # Entire application (Docker build context)
├── prisma/ # Schema, migrations, seed
├── public/docs/ # THIS documentation (static HTML)
├── src/app/ # Routes, server actions
├── src/components/ # UI
├── src/domain/ # Types + Zod
├── src/server/ # services, repositories, ai, auth, integrations
└── tests/ # unit, integration, e2e
Markdown under repo docs/ (ADRs, meeting notes) remains the
historical design record. Prefer linking to those for deep decisions;
this HTML site is the navigable export for users and agents.
Architecture → · Implementation detail → · How to continue development →
Technical documentation
One Next.js service, layered domain code, metadata and vocabulary as data.
Browser
│ /docs → static HTML (public/docs)
│ UI → Server Components + server actions
▼
Next.js 15 (App Router) — ECS Fargate "web", port 3000
├── app/ routes, actions
├── server/services/ business rules
├── server/repositories/ Prisma only
├── server/ai/ providers, extraction, scoring
├── server/integrations/ MaRLay client (mock today)
└── domain/ Zod + types
▼
PostgreSQL (schema assaylink)
/health — 200 without touching the DB (ALB health check)./api/readiness — DB-touching readiness for humans/dashboards.| Layer | May import | Must not |
|---|---|---|
app/**, components |
services, domain | Prisma, repositories |
server/services/** |
repositories, domain, ai | React / Next request APIs beyond auth helpers |
server/repositories/** |
db.ts, domain |
services (no cycles) |
server/ai/** |
domain, repositories (read) | services |
domain/** |
zod only | everything else |
User — demo users with roles.Vocabulary / VocabularyTerm — governed terminology.MetadataSection — steward-named groups of fields (authoring pages by these).MetadataFieldDefinition — the assay metadata model as rows.MetadataRule — conditional show / require / restrict-term logic.AssayTemplate / AssayTemplateVersion — definitions + immutable snapshots.TemplateFieldValue — values for a version (term and/or text).TemplateReview — steward decisions and requested changes.SuggestedTerm — pending vocabulary candidates.ProjectAssayLink — project ↔ template version + context.ChangeLog — audit trail.
Full narrative: repo docs/domain-model.md and ADRs 0002, 0007–0010.
Schema: src/web/prisma/schema.prisma. AssayLink does not store
experimental execution data.
server/ai/provider.ts.mock (default), claude, azure-openai.metadata-extractor.ts), parse via
parse-raw-extraction.ts.
template-recommender.ts, scoring.ts).SuggestedTerm, never auto-activated.See docs/adr/0005-ai-provider-abstraction.md and docs/ai-recommendation.md.
POC: signed cookie + role switcher (ENABLE_ROLE_SWITCHER).
Pages: guardCapability. Mutations:
requireCapability. Capability map in
server/auth/roles.ts. Production path: ALB OIDC header →
map groups to roles in current-user.ts only
(docs/adr/0006-auth-strategy.md).
config.yml. Never edit terraform/.docker build -f src/web/Dockerfile ./src/web —
build context is the service folder.
DATABASE_URL may be composed from POSTGRES_*
parts at runtime (server/db-url.ts).
| ADR | Topic |
|---|---|
| 0001 | Tech stack (Next.js monolith under src/web) |
| 0002 | Domain model / metadata as data |
| 0003 | Template versioning (subtypes deferred) |
| 0004 | Vocabulary governance |
| 0005 | AI provider abstraction |
| 0006 | Auth strategy |
| 0007 | Metadata sections |
| 0008 | Conditional metadata rules |
| 0009 | Metadata Quality Score |
| 0010 | Definitions, not experimental results |
Source files: docs/adr/*.md in the repository root.
Technical documentation
Concrete map of routes, services, seed data, and tests.
./dev.sh # Postgres, migrate, seed, next dev
./dev.sh --reset # wipe DB then seed
./dev.sh --no-seed # skip seed
App: http://localhost:3000. Docs:
http://localhost:3000/docs/.
Work only inside src/web for npm scripts.
| Path | Purpose | Capability |
|---|---|---|
/ | Role dashboard | — |
/templates | Grouped catalogue | — |
/templates/new, /templates/[id]/edit | Author / edit draft | template:create / ownership |
/templates/[id] | Detail + lifecycle actions | view; mutate gated |
/matcher | Free-text match (non-project roles) | — |
/projects, /projects/[id], .../new | Project-first flow | project:link |
/steward/review-queue | Review queue | template:review |
/steward/model, /steward/model/rules, /steward/model/sections/[key], /steward/fields/[key] | Assay definition model | field:manage |
/steward/vocabulary, suggestions, changes | Vocabulary governance | vocabulary:manage |
/docs/ | Static documentation | public |
/health, /api/readiness | Health / readiness | public |
/forbidden | Capability denial | — |
| Module | Responsibility |
|---|---|
template-service.ts | Create/update drafts, status transitions, field values |
review-service.ts | Queue + Metadata Quality Score inputs |
vocabulary-service.ts | Terms, suggestions approval |
field-service.ts | Metadata field definitions |
section-service.ts | Metadata sections |
rule-service.ts | Conditional metadata rules |
matcher-service.ts | Extract → recommend → rule-gap insights |
project-link-service.ts | ProjectAssayLink CRUD |
wizard-service.ts | Wizard payload / edit hydration |
catalogue-service.ts | Grouped catalogue |
integrations/marlay.ts | Project master-data client (mock) |
Statuses and legal transitions live in domain/status.ts
(DRAFT, SUBMITTED, IN_REVIEW,
CHANGES_REQUESTED, APPROVED, PUBLISHED,
REJECTED, …). Editable when
isTemplateEditable is true (draft / changes requested).
src/web/data/seed-sources/*.json (examples, not the schema).PK In Vitro Stability.json is seeded as a Draft needing review
(CIA-style block despite the filename) — source preserved verbatim.
prisma/seed/data/.| Level | Command / location |
|---|---|
| Unit | tests/unit/* — matching, scoring, validation, rule evaluation, quality score, diagram layout, … |
| Integration | tests/integration/* — needs docker-compose Postgres |
| E2E | npm run test:e2e — Playwright core flows |
CI: .github/workflows/ci.yml — lint, typecheck, unit tests, build.
src/web/public/docs/ (served as
/docs/index.html; bare /docs redirects there
via next.config.ts).
assaylink-docs.html — every page plus
base64 screenshots in one ~7 MB file, rebuilt with
npm run docs:bundle
(scripts/build-docs-bundle.mjs). It re-assembles the
per-page HTML, so prose is never duplicated.
npm run docs:screenshots →
scripts/capture-docs-screenshots.mjs. Run
docs:bundle afterwards so the download matches.
Technical documentation
Instructions for a third-party coding agent picking up this repository cold. Scope, constraints, and safe next work.
CLAUDE.md and this page before large edits.docs/meeting-notes.md) and the product
brief as product-context authoritative for workflow and scope.
server/ai/; validate with Zod; route new
terms to SuggestedTerm.
terraform/. Infra changes go through
config.yml only.
src/web/.
current-user.ts without changing
call sites of requireCapability.
npm run docs:screenshots).
src/web/.cd src/web
npm run lint
npm run typecheck
npm test
# optional: npm run test:e2e
# optional: npm run docs:screenshots # if UI changed meaningfully
| Term | Meaning |
|---|---|
| Assay Template | Reusable assay definition; has versions |
| Assay Template Version | Immutable-once-approved metadata snapshot |
| Project Assay Link | Project’s use of a version + context; not a run |
| MaRLay | External project master data; six-digit ids |
| Vocabulary / Term | Governed controlled list / concept |
| Metadata Field Definition | Field in the assay metadata model |
| Metadata Section | Steward-named group of fields; wizard pages by these |
| Metadata Rule | When/then logic: show, require, restrict vocabulary terms |
| Metadata Quality Score | Completeness, vocabulary compliance, rule consistency — not experimental results |
| Template Review | Steward decision on a submitted version |
| Suggested Term | Candidate awaiting steward approval |