Before contributing, ensure you have the following installed (this section is the canonical source for toolchain versions):
- .NET SDK: 10.0.0 or later
- Node.js: 18.x, 22.x, or 23.x (LTS versions recommended)
- npm: 10.x or later
- Fork this repository
- Clone your fork locally
- Configure the demo environment variables as described in the Environment Configuration section below.
If you prefer, you can use direnv, as documented in the Environment Configuration section below.
dotnet runOpen your browser to http://localhost:5173
GenPRES uses FAKE (F# Make) as its build automation tool. The build configuration lives in two files at the repository root:
Build.fs– defines all FAKE build targets (tasks) and their dependency chainsHelpers.fs– helper functions for running processes (dotnet, npm, docker) in the build
When you type dotnet run from the repository root, .NET executes Build.fsproj, which is an F# console application that initialises the FAKE execution context. FAKE then reads the target name from the command-line arguments (defaulting to Run when none is given) and executes the corresponding target and all of its declared dependencies.
dotnet run [target]
│
└─► Build.fsproj (F# console app)
│
└─► FAKE target engine
│
├─► resolves target dependency chain
└─► executes each target step
For example, dotnet run (no target) runs the Run target, which depends on:
Clean → RestoreClient → Build → Run (server + Fable watcher in parallel).
| Command | Target | Description |
|---|---|---|
dotnet run |
Run |
Start server + Fable/Vite dev server with hot reload (default) |
dotnet run list |
(special) | List all available FAKE targets |
dotnet run Build |
Build |
Compile the entire solution (GenPRES.sln) |
dotnet run Clean |
Clean |
Remove deploy/ and dist/ artefacts, delete Fable-generated .jsx files |
dotnet run Bundle |
Bundle |
Production build: publish server, compile client, copy data |
dotnet run ServerTests |
ServerTests |
Run all F# unit tests (Expecto) with quiet logging |
dotnet run TestHeadless |
TestHeadless |
Build and run tests without launching a browser |
dotnet run WatchTests |
WatchTests |
Run tests in watch mode (re-runs on file changes) |
dotnet run Format |
Format |
Format all F# source files using Fantomas |
dotnet run DockerRun |
DockerRun |
Run the pre-built Docker image locally |
Clean ──► RestoreClient ──► Bundle
Clean ──► RestoreClient ──► Build ──► Run
RestoreClient ──► Build ──► TestHeadless
RestoreClient ──► Build ──► WatchTests
The Run target starts two long-running processes in parallel:
- Server –
dotnet run --no-restoreinsrc/Informedica.GenPRES.Server/- Saturn/Giraffe HTTP server on port
8085
- Saturn/Giraffe HTTP server on port
- Client –
dotnet fable watch … --run npx viteinsrc/Informedica.GenPRES.Client/- Fable compiles F# → JavaScript, Vite serves the client on
http://localhost:5173with Hot Module Replacement (HMR)
- Fable compiles F# → JavaScript, Vite serves the client on
Output from both processes is printed concurrently with colour-coded prefixes (server:, client:).
The CI pipeline is defined in .github/workflows/build.yml and runs on every push or pull request to master across three operating systems:
| Matrix | OS |
|---|---|
| ubuntu-latest | Linux |
| windows-latest | Windows |
| macOS-latest | macOS |
Pipeline steps:
- Checkout –
actions/checkout@v4 - Install .NET SDK – installs .NET 10.0 via
actions/setup-dotnet - Tool restore –
dotnet tool restore(installs paket, fable, fantomas, husky from.config/dotnet-tools.json) - Format check –
dotnet fantomas --check .(fails the build on unformatted code) - Test execution –
dotnet run ServerTests(runs all Expecto tests)
Environment variables set in CI (from .github/workflows/build.yml):
env:
CI: true # Disables interactive prompts
GENPRES_DEBUG: 1 # Enables debug logging during test runsThe pipeline does not set GENPRES_URL_ID, so tests run against demo/cached data only. Production data is never accessed in CI.
The repository ships a .vscode/settings.json with Ionide (F# language support) settings. To work effectively:
- Install the Ionide for F# extension (
ionide.ionide-fsharp) - Open the repository root folder in VS Code
- Ionide will use
GenPRES.slnto discover projects and provide IntelliSense
Running from VS Code terminal:
# Start full application (server + client)
dotnet run
# Run tests
dotnet run ServerTests
# Build only
dotnet run BuildYou can also add custom VS Code tasks in .vscode/tasks.json if you want keyboard-shortcut access to build targets.
- Open
GenPRES.slnin Rider (not the folder — open the.slnfile) - Rider will restore packages and index the solution automatically
Running the application from Rider:
The most reliable approach in Rider is to use the integrated terminal:
dotnet runAlternatively, you can create a Run Configuration manually:
- Type: .NET Project
- Project:
Build(the rootBuild.fsproj) - Program arguments: (leave empty to start with the default
Runtarget)
Running individual targets:
Add the target name as a program argument, for example ServerTests to run the tests.
Because the application starts the server process indirectly through FAKE, attaching the Rider debugger requires a two-step approach:
Option 1 – Attach to running process (recommended):
- Start the server normally:
dotnet runin the terminal - In Rider: Run → Attach to Process and select the
Informedica.GenPRES.Serverprocess - Set breakpoints in the server source files; Rider will break when they are hit
Option 2 – Run server directly:
- In Rider, create a Run/Debug Configuration of type .NET Project:
- Project:
Informedica.GenPRES.Server - Working directory:
src/Informedica.GenPRES.Server
- Project:
- Start the client separately in a terminal:
dotnet fable watch -o output -s -e .jsx --run npx vitefromsrc/Informedica.GenPRES.Client/ - Use Rider's Debug button to launch the server with the full debugger attached
Note: When running the server directly (Option 2), environment variables from
.envare loaded automatically byEnv.loadDotEnv()in the server startup code, so no additional IDE configuration is needed for environment variables.
- Create a
.vscode/launch.jsonfile (if it does not exist):
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch GenPRES Server",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "dotnet: build",
"program": "${workspaceFolder}/src/Informedica.GenPRES.Server/bin/Debug/net10.0/Informedica.GenPRES.Server.dll",
"args": [],
"cwd": "${workspaceFolder}/src/Informedica.GenPRES.Server",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
}
}
]
}- Press F5 to start the server with the debugger attached
- Start the client in a separate terminal:
dotnet fable watch -o output -s -e .jsx --run npx vitefromsrc/Informedica.GenPRES.Client/
Tip: The C# Dev Kit or the .NET Install Tool extension may be required depending on your VS Code setup.
GenPRES/
├── .github/ # GitHub configuration and workflows
│ ├── ISSUE_TEMPLATE/ # Issue templates
│ ├── PULL_REQUEST_TEMPLATE/ # PR templates
│ ├── instructions/ # Development instructions
│ └── workflows/ # CI/CD workflows
├── .husky/ # Git hooks
├── .idea/ # JetBrains IDE configuration
├── .vscode/ # VS Code configuration
├── benchmark/ # Performance benchmarks
├── data/ # Application data
│ ├── cache/ # Cached data files
│ ├── config/ # Configuration files
│ ├── data/ # JSON data files
│ └── zindex/ # Z-Index drug database files
├── deploy/ # Deployment scripts and configurations
├── docs/ # Documentation
│ ├── code-reviews/ # Code review documents
│ ├── data-extraction/ # Data extraction documentation
│ ├── domain/ # Domain documentation
│ ├── implementation-plans/ # Implementation plans
│ ├── literature/ # Research literature
│ ├── mdr/ # Medical Device Regulation documentation
│ │ ├── design-history/ # Design history files
│ │ ├── interface/ # Interface specifications
│ │ ├── post-market/ # Post-market surveillance
│ │ ├── requirements/ # Requirements documentation
│ │ ├── risk-analysis/ # Risk management
│ │ ├── usability/ # Usability engineering
│ │ └── validation/ # Validation documentation
│ ├── roadmap/ # Project roadmap
│ └── scenarios/ # Clinical scenarios
├── scripts/ # Utility scripts
└── src/ # Source code
├── Informedica.Agents.Lib/ # Agent-based concurrency library
├── Informedica.DataPlatform.Lib/ # Data Platform integration
├── Informedica.FHIR.Lib/ # FHIR resource conversion
├── Informedica.FTK.Lib/ # Adult formulary parsing library
├── Informedica.GenCORE.Lib/ # Core domain library
├── Informedica.GenFORM.Lib/ # Formulary management library
├── Informedica.GenORDER.Lib/ # Order processing library
├── Informedica.GenPRES.Client/ # Frontend application
│ ├── Components/ # UI components
│ ├── Pages/ # Page components
│ ├── Views/ # View components
│ ├── output/ # Compiled JavaScript output
│ └── public/ # Static assets
├── Informedica.GenPRES.Server/ # Backend application
│ ├── Properties/ # Server properties
│ ├── Scripts/ # Server scripts
│ └── data/ # Server data directory
├── Informedica.GenPRES.Shared/ # Shared types and API protocol
├── Informedica.GenSOLVER.Lib/ # Constraint solver library
├── Informedica.GenUNITS.Lib/ # Units of measurement library
├── Informedica.HIXConnect.Lib/ # HIX Connect integration
├── Informedica.Logging.Lib/ # Logging utilities
├── Informedica.MCP.Lib/ # Model Context Protocol for LLM integration
├── Informedica.MetaVision.Lib/ # MetaVision integration
├── Informedica.NKF.Lib/ # Pediatric formulary parsing library
├── Informedica.NLP.Lib/ # Natural Language Processing for rule extraction
├── Informedica.OTS.Lib/ # Ontology Terminology Server integration
├── Informedica.Utils.Lib/ # Utility functions
├── Informedica.ZForm.Lib/ # Z-Index form library
└── Informedica.ZIndex.Lib/ # Z-Index database library
Build.fs/Build.fsproj- Build automationGenPRES.sln- Solution fileDockerfile- Docker containerizationpaket.dependencies- Package managementglobal.json- .NET SDK version
README.md- Project overviewCHANGELOG.md- Version historyCONTRIBUTING.md- Contribution guidelinesCODE_OF_CONDUCT.md- Code of conductDEVELOPMENT.md- Development guide (this file)GOVERNANCE.md- Project governanceMAINTAINERS.md- Maintainer informationROADMAP.md- Project roadmapSECURITY.md- Security policySUPPORT.md- Support informationWARP.md- Warp AI agent documentationdocs/mdr/design-history/architecture.md- Technical architecturedocs/domain/- Domain model specificationsdocs/user-guide/- Multilingual user guide (English, Nederlands)
.github/- GitHub configurations (issue/PR templates, workflows, development instructions)benchmark/- Performance benchmarking suitedata/- Application data (drug cache, configuration, clinical data, Z-Index database)docs/- Comprehensive documentation:docs/domain/- Domain model specifications (Core Domain, GenFORM, GenORDER, GenSOLVER)docs/mdr/- MDR compliance (design history, requirements, risk analysis, validation)docs/scenarios/- Clinical scenarios
src/- Source code (client, server, and F# libraries)
Each Informedica.*.Lib directory contains:
- Core F# source files
Scripts/- Interactive F# scripts for testingNotebooks/- Jupyter/Polyglot notebooks (where applicable)paket.references- Package dependencies*.fsproj- F# project file
For complete architectural documentation, see:
- Architecture Overview: Technical stack, server/client structure, Docker hosting, and build configuration
- Core Domain Model: Transformation pipeline, constraint-based architecture, and domain concepts
- GenFORM: Free text to Operational Knowledge Rules (OKRs)
- GenORDER: OKRs to Order Scenarios
- GenSOLVER: Constraint solving engine
This project is built on the SAFE Stack:
- Informedica.GenPRES.Server: F# with Saturn
- Informedica.GenPRES.Client: F# with Fable and Elmish
- Testing: Expecto with FsCheck for property-based testing
- Build: .NET 10.0
For complete library specifications including capabilities and dependencies, see GenFORM Appendix B.3.
Key libraries in dependency order:
- Informedica.Utils.Lib: Shared utilities, common functions
- Informedica.Agents.Lib: Agent-based execution (MailboxProcessor)
- Informedica.Logging.Lib: Concurrent logging
- Informedica.NLP.Lib: Natural Language Processing for structured rule extraction
- Informedica.OTS.Lib: Google Sheets/CSV and Ontology Terminology Server integration
- Informedica.GenUNITS.Lib: Unit-safe calculations
- Informedica.GenSOLVER.Lib: Quantitative constraint solving
- Informedica.GenCORE.Lib: Core domain model
- Informedica.ZIndex.Lib: Medication and product database
- Informedica.ZForm.Lib: Z-Index dosing reference data
- Informedica.NKF.Lib: Kinderformularium dose rule extraction
- Informedica.FTK.Lib: Farmacotherapeutisch Kompas dose rule extraction
- Informedica.GenFORM.Lib: Operational Knowledge Rules (OKRs)
- Informedica.GenORDER.Lib: Clinical order scenarios and execution
- Informedica.MCP.Lib: Model Context Protocol for LLM integration
- Informedica.FHIR.Lib: FHIR resource conversion
- Informedica.DataPlatform.Lib: Data Platform integration
- Informedica.HIXConnect.Lib: HIX Connect integration
- Informedica.MetaVision.Lib: MetaVision integration
- Informedica.GenPRES.Shared: Shared types and API protocol
- Informedica.GenPRES.Server: Server API and orchestration
- Informedica.GenPRES.Client: Web-based clinical UI
Important: an opt-in strategy is used in the .gitignore file, i.e. you have to specifically define what should be included instead of the other way around!!
This project follows specific organizational patterns:
- Library Structure: Use the
Informedica.{Domain}.{Lib/Server/Client}naming convention - Domain Libraries: GenSOLVER, GenORDER, GenUNITS, GenCORE
- Separate Test Projects: Each library has its own test project
- Opt-in .gitignore: You must explicitly define what should be included!!
Follow the F# Coding Instructions for code style, formatting, type design, error handling, testing, and documentation guidelines.
Follow the Commit Message Instructions for conventional commit format, types, scopes, and examples.
When contributing to medical functionality:
- Patient Safety First: All changes affecting dosage calculations, medication lookup, or clinical decision support must be thoroughly tested
- Precision Matters: Use appropriate units of measure and maintain calculation accuracy
- Validation Required: Implement comprehensive input validation for medical data
- Error Handling: Provide clear, actionable error messages for medical professionals
- MDR Compliance: Ensure all medical-related changes align with Medical Device Regulation requirements
For mathematical operations, units of measure, performance, and testing guidelines, see F# Coding Instructions.
-
Fork the repository
-
Clone your fork locally:
git clone https://github.com/your-username/GenPRES.git -
Set up upstream remote:
git remote add upstream https://github.com/informedica/GenPRES.git -
Before starting work, sync your fork:
git checkout master git fetch upstream git merge upstream/master git push origin master
-
Create a feature branch:
git checkout -b feat/your-feature-name -
Make changes following our coding guidelines
-
Commit using conventional commit messages
git commit -m "feat(scope): description" -
Check that you are still in sync with upstream:
git fetch upstream git merge upstream/master
-
Push to your fork
git push origin feat/your-feature-name -
Create a pull request to the main repository
-
After PR is merged, delete your feature branch locally and remotely:
git checkout master git pull upstream master git push origin --delete feat/your-feature-name git branch -d feat/your-feature-name
-
Repeat for new features or fixes
This project uses an opt-in strategy for .gitignore:
- You must explicitly define what should be included
- When adding new files, ensure they're properly included in Git
- Proprietary medication cache files are excluded for licensing reasons
This project uses a .env file at the project root as the single source of truth for environment variables. The .env file is excluded from git by the opt-in .gitignore strategy, so secrets are never committed.
- Copy the example file:
cp .env.example .env - Edit
.envand fill in theGENPRES_URL_IDvalue (ask a team member for the production URL ID)
The .env file uses standard KEY=VALUE format:
GENPRES_URL_ID=<your-url-id> # Google Sheets data URL ID (required)
GENPRES_LOG=i # Logging level: 0=off, d=debug, i=info, w=warning, e=error
GENPRES_PROD=0 # Production mode: 0=demo (safe default), 1=production data
GENPRES_DEBUG=1 # Debug mode: 0=off, 1=onEnvironment variables are resolved in this priority order (highest first):
- Already-set environment variable (from shell, CI, Docker) — takes precedence
.envfile — loaded by shell scripts orEnv.loadDotEnv()in F#- Hardcoded default in source code — safe fallback (demo data)
This means you can always override .env values by setting an environment variable directly.
- Shell: Source
.envmanually withset -a; source .env; set +abefore running commands. - F# scripts (FSI): Scripts call
Informedica.Utils.Lib.Env.loadDotEnv()which searches upward for.envfrom the current directory. - IDEs (Rider, VS Code): The
Env.loadDotEnv()call in scripts ensures variables are available even when the IDE doesn't inherit shell environment. - Docker: Source
.envbeforedocker buildto passGENPRES_URL_IDas a build argument.
Missing GENPRES_URL_ID: Will cause "cannot find column" errors when the application tries to load resources from Google Sheets. Make sure your .env file exists and contains a valid GENPRES_URL_ID.
Incorrect GENPRES_PROD value: Setting this to anything other than 0 in development may cause authentication or data access issues.
For background on this approach, see Issue #44.