The single web platform where OneCGIAR Initiatives, Centers, and partners plan, capture, qualify, and submit reportable results for the CGIAR portfolio, and where PMU / portfolio leads consolidate those results into Type-One Reports, IPSR pathways, bilateral exports, and platform-level narratives.
This monorepo contains both halves of the system:
- Backend —
onecgiar-pr-server/— NestJS 11 + TypeORM + MySQL, deployed to AWS Lambda (Serverless) or Docker. - Frontend —
onecgiar-pr-client/— Angular 19 + PrimeNG 19, served as a static SPA behind Nginx.
Hosted documentation: PRMS Wiki on Mintlify — browse the project documentation online, including module references and onboarding material.
- What PRMS does
- Who uses it
- Architecture at a glance
- Repository layout
- Quick start
- Build, test, deploy
- Documentation map
- Conventions
- Security
- External systems
- Contributing
- License
PRMS captures typed results across the CGIAR portfolio (knowledge product, capacity sharing, innovation development, innovation use, innovation package / IPSR, policy change, others) with shared common fields — identity, ToC alignment, geography, partners, DAC scores, evidence — and drives them through a structured submission workflow (Editing → Quality Assessed → Submitted).
Core capabilities:
- Typed result capture & lifecycle for all result types, including IPSR pathway steps and policy change.
- Quality assurance tooling — review queue, drawer, structured comments, status transitions, review history.
- Phase / version management (reporting year + sub-phases) with snapshot-on-rollover semantics.
- CLARISA integration as a consumer of master data (centers, initiatives, partners, countries, indicators) with scheduled syncs.
- Theory of Change alignment — attach results to ToC outcomes sourced from external ToC services.
- Bilateral and platform-report payload surfaces for downstream consumers, with stable typed shapes per result type.
- Admin tooling — roles, AD users, global parameters, phases, delete/recover, narratives, initiative-entity maps.
- Real-time notifications and updates via Pusher + WebSockets, plus transactional email.
- AI helpers for assisted authoring.
For the full product picture — problem statement, goals, success metrics, scope, acceptance criteria — see docs/prd.md.
| Persona | What they do in PRMS |
|---|---|
| Result submitter (Initiative / Center staff) | Create typed results, attach evidence, align ToC, mark partners and geography, fix QA feedback. |
| Quality Assurance reviewer | Review submissions, comment by field, advance or send back. |
| PMU / portfolio lead | Run Type-One Reports, oversee IPSR pathways, edit global narratives, monitor submission progress per phase. |
| Platform admin | Manage roles, AD users, CLARISA syncs, phases / versioning, delete-and-recover, notification settings, global parameters. |
| Bilateral / platform-report consumers (non-interactive) | Read the typed payload APIs at /api/bilateral/* and /api/platform-report/*. |
Browser (Angular 19 SPA)
│
│ HTTPS, custom `auth` header (JWT)
▼
API Gateway / Nginx
│
▼
NestJS app (Lambda or container)
│
┌──────────────────────┼──────────────────────┐
│ │ │
TypeORM ► MySQL AWS SDK HTTP integrations
│ │
├── Cognito ├── CLARISA (catalogs)
├── S3 / SharePoint ├── Theory of Change
├── DynamoDB (logs) ├── MQAP (KP attrs)
└── CGSpace (handles)
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
LDAP RabbitMQ Pusher / Sockets
(AD) (reporting-metadata) (real-time)
Highlights:
- Auth header is a custom
auth: <JWT>— NOTAuthorization: Bearer. The Angular interceptor attaches it and the NestJS middleware verifies it. /api/bilateral/*and/api/platform-report/*are JWT-excluded — they're headless typed surfaces protected at the perimeter.- MySQL is the system of record. DynamoDB is operational logs only.
- AWS Cognito + Active Directory (LDAP) for identity. PRMS doesn't own user provisioning.
Full technical blueprint: docs/detailed-design/detailed-design.md.
onecgiar_pr/
├── onecgiar-pr-server/ NestJS 11 backend
│ ├── src/ Source tree (see src/CLAUDE.md for in-tree navigation)
│ ├── docs/ Server-specific reference (bilateral contract)
│ ├── scripts/ Migration checks, etc.
│ ├── serverless.yaml AWS Lambda deploy config
│ ├── Dockerfile Node 20 multi-stage build
│ └── CLAUDE.md Backend package guide
├── onecgiar-pr-client/ Angular 19 frontend
│ ├── src/ Source tree (see src/CLAUDE.md for in-tree navigation)
│ ├── cypress/ E2E tests
│ ├── guides/ Lint & format setup notes
│ ├── nginx.conf Production static-server config
│ ├── Dockerfile Container build
│ └── CLAUDE.md Frontend package guide
├── docs/ SDD constitutional baseline
│ ├── prd.md Product Requirements Document
│ ├── system-design/ UI/UX system blueprint
│ ├── detailed-design/ Technical implementation blueprint
│ └── specs/ Module-level specs + general-setup templates
├── .github/ CI workflows
├── .husky/ Git hooks
├── .cursorrules Hard security rule (no secrets in logs)
├── CLAUDE.md Root operating guide
├── sonar-project.properties SonarCloud config
└── README.md
- Node.js 20.x (matches the Docker image and Lambda runtime).
- npm (bundled with Node).
- A running MySQL 8.x instance (for the server).
- Optional: Docker for containerized runs; AWS CLI + Serverless Framework for Lambda deploys.
git clone https://github.com/AllianceBioversityCIAT/onecgiar_pr.git
cd onecgiar_pr
# Root: installs husky hooks
npm ci && npm run prepare
# Backend
cd onecgiar-pr-server && npm ci && cd ..
# Frontend
cd onecgiar-pr-client && npm ci && cd ..Both apps read from environment variables. Common keys:
- Backend (
onecgiar-pr-server/.env):PORT,DB_HOST,DB_PORT,DB_NAME,DB_USER_NAME,DB_USER_PASS,JWT_SKEY,JWT_EXPIRES, plus RabbitMQ, AWS, Cognito, AD, CLARISA, MQAP, SharePoint vars. - Frontend (
onecgiar-pr-client/src/environments/environment.ts):apiBaseUrl, Cognito + Pusher keys, etc.
Secrets live in environment variables, AWS Secrets Manager, or GitHub Secrets. Never commit .env files or paste tokens / webhook URLs into logs or commits — see Security.
# Backend (port from env.PORT, default 3000)
cd onecgiar-pr-server
npm run start:dev # auto-reload
# Swagger at http://localhost:3000/api
# Frontend (port 4200)
cd onecgiar-pr-client
npm start # ng serve, http://localhost:4200npm run build # nest build
npm run start # production mode
npm run start:prod # serve dist/main
npm run test # Jest unit tests
npm run test:cov # with coverage (thresholds: 5/20/35/40)
npm run test:e2e # e2e tests (test/jest-e2e.json)
npm run migration:generate -- ./src/migrations/<Name> # generate migration
npm run migration:run # apply pending migrations
npm run migration:revert # roll back last migration
npm run migration:check # local guard
npm run migration:check:ci # CI guard (blocks merges)
npm run lambda:test # serverless offline
npm run lambda:deploy # sls deploy -vContainerized:
cd onecgiar-pr-server
docker build -t prms-server .
docker run --env-file .env -p 3000:3000 prms-servernpm run build # production build
npm run build:dev # development build
npm run watch # dev rebuild on change
npm run test # Jest unit tests
npm run test:coverage # with coverage (thresholds: 50/60/60/60)
npm run lint # ng lint
npm run lint:fix # auto-fix
npm run cypress:open # Cypress GUI
npm run cypress:run # headless
npm run cypress:run:record # recorded runContainerized (static via Nginx):
cd onecgiar-pr-client
docker build -t prms-client .
docker run -p 8080:80 prms-clientCI runs lint, tests, build, migration:check:ci, and SonarCloud (sonar-project.properties). Husky pre-commit hook is installed by npm run prepare at the repo root.
Hosted reference: the full PRMS documentation is also published as a navigable wiki on Mintlify — PRMS Wiki. Use it as the entry point when you don't want to clone the repo.
PRMS uses Spec-Driven Development (SDD). The hierarchy is:
README.md ← you are here
└── CLAUDE.md (operating instructions)
├── docs/prd.md Product baseline (problem, personas, goals, AC-1..AC-9)
├── docs/system-design/design.md UI/UX system blueprint (tokens, components, flows, a11y)
├── docs/detailed-design/ Technical implementation blueprint
├── docs/specs/general-setup/ Templates that module specs MUST follow
├── docs/specs/<module>/ Module-level specs (requirements + design + task)
├── onecgiar-pr-server/CLAUDE.md Backend package guide
│ └── src/CLAUDE.md In-tree navigation, base classes, patterns
└── onecgiar-pr-client/CLAUDE.md Frontend package guide
└── src/CLAUDE.md In-tree navigation, route tables, patterns
| I want to… | Read |
|---|---|
| Browse the docs online without cloning | PRMS Wiki on Mintlify |
| Understand the product, scope, goals, metrics, personas | docs/prd.md |
| Understand UI/UX rules, tokens, components, screens | docs/system-design/design.md |
| Understand the technical architecture | docs/detailed-design/detailed-design.md |
| Write a new module spec | Start from docs/specs/general-setup/ |
| Work on the backend | onecgiar-pr-server/CLAUDE.md → onecgiar-pr-server/src/CLAUDE.md |
| Work on the frontend | onecgiar-pr-client/CLAUDE.md → onecgiar-pr-client/src/CLAUDE.md |
| Understand bilateral payload contracts | onecgiar-pr-server/docs/bilateral-result-summaries.en.md |
These commands operate against the SDD baseline above:
/sdd-constitution— refresh the project-level baseline (PRD, system design, detailed design, templates, root CLAUDE.md)./sdd-specify— generate a module spec triplet (requirements.md,design.md,task.md) underdocs/specs/<module>/./sdd-execute— run the tasks defined in atask.md./sdd-validate— check a module spec follows the templates and references the baseline./sdd-test— drive test coverage for a spec.
Format: <emoji> <type>(<scope>) [ticket]: <description>
| Emoji | Type | Use |
|---|---|---|
| ✨ | feat |
New features or functionality |
| ♻️ | refactor |
Refactor without behavior change |
| 🔧 | fix |
Bug fixes |
| 🎨 | style |
UI / formatting / styling |
Scope = component or service name (bilateral.service, result-review-drawer, phase-management-table). Ticket optional (P2-2498).
Examples:
✨ feat(knowledge-product-info): Integrate FieldsManagerService and enhance test coverage
♻️ refactor(result-review-drawer) P2-2498: Extract toNum function for number coercion
🔧 fix(submissions.service): Correct formatting and remove unnecessary comment
🎨 style(share-request-modal) P2-2498: Update modal title layout and button styles
master— production-tracking.staging— integration branch that merges intomastervia PR.- Open PRs against
stagingormasterper the team's release cadence.
- Backend: branches 5%, functions 20%, lines 35%, statements 40% — floors, aim higher.
- Frontend: branches 50%, functions 60%, lines 60%, statements 60%.
- Backend: module-per-feature under
src/api/<feature>/with the standard NestJS trio (module.ts,controller.ts,service.ts), DTOs validated withclass-validator, response envelope viaResponseInterceptor, errors viaHttpExceptionFilter. Schema changes only via TypeORM migrations. - Frontend: page-module-per-feature under
src/app/pages/<feature>/, HTTP methods namedHTTP_METHOD_descriptiveName, state in Angular signals (no NgRx), strings viainternationalization/(P22 vs P25 vocabulary), tokens fromsrc/styles/colors.scsswith--pr-*prefix mirrored insrc/app/theme/reportingTheme.ts.
Full conventions in the package guides.
PRMS has a hard, project-wide rule: never print, log, or echo tokens, webhook URLs, API keys, passwords, AD/Cognito credentials, DB credentials, or sensitive environment variables anywhere — code, scripts, CI workflows, docs, debug output, error messages.
The rule applies equally to:
- Backend services, repositories, scripts, and CI pipelines.
- Frontend components, services, and telemetry.
- Documentation and example commands.
The full policy lives in .cursorrules. Secrets live in environment variables, AWS Secrets Manager, or GitHub Secrets. PRs MUST NOT add .env content.
Authentication uses a custom auth: <JWT> header verified by JwtMiddleware. Authorization uses ValidRoleGuard + @Roles() decorator with the role hierarchy in onecgiar-pr-server/src/shared/constants/role-type.enum.ts.
PRMS integrates with — but does not own — these systems:
| System | How PRMS uses it |
|---|---|
| CLARISA | Catalog source (institutions, centers, initiatives, countries, regions, indicators, etc.) — read-only via HTTP + scheduled syncs. |
| Theory of Change services | ToC trees and outcomes (/toc/*). PRMS aligns results to ToC; it does not author ToC. |
| AWS Cognito + Active Directory (LDAP) | Identity and authentication. PRMS doesn't provision users. |
| CGSpace | Stable handle for knowledge products. |
| MQAP | External knowledge-product attribute lookup. |
| AWS S3 / SharePoint | Evidence files and PRMS documents. |
| AWS DynamoDB | Operational logs only (/logs/*). |
| RabbitMQ | reporting-metadata-export async pipeline. |
| Pusher + WebSockets | Real-time notifications and updates. |
| Hotjar + Microsoft Clarity | Client-side session telemetry. |
- Read
CLAUDE.md(root operating instructions) and the relevant package guide. - Find or open a spec under
docs/specs/<module>/— start fromdocs/specs/general-setup/if creating a new one. - Implement per the conventions in the package guides (
onecgiar-pr-server/src/CLAUDE.mdfor backend code,onecgiar-pr-client/src/CLAUDE.mdfor frontend code). - Run
npm run lint+npm run test(+npm run migration:checkon backend). Keep coverage above thresholds. - Open a PR against
staging(ormasterper cadence) using the commit format above. - If the change touches
/api/bilateral/*or/api/platform-report/*, add a change-log row inonecgiar-pr-server/docs/bilateral-result-summaries.en.md.
Pre-commit hooks (Husky) run lint-staged checks; please do not bypass them.
See LICENSE.