avniproject / avniproject/rules-config

Eligibility rules: guard + bind enrolment for enrolment-scoped conditions (fixes RuleCondition crash)

Open
#40 0 comments 0 reactions 0 assignees View on GitHub
bug enhancement
Dominant language
JavaScript
Stars
0
Forks
3
PR merge metrics
No merged PRs in 30d

Description

Tracks the `rules-config` changes required by avniproject/avni-webapp#1791 (declarative rule builder: enrolment-scoped conditions in eligibility rules). rules-config is the dependency and must land **first** — avni-webapp and avni-client both pin it.

## Problem

Eligibility rules (`EncounterEligibilityCheck`, `EnrolmentEligibilityCheck`) run in an **individual-only** context (`{entity: individual}`, no enrolment — `avni-client RuleEvaluationService.js:981`). But the declarative codegen lets an enrolment-scoped concept scope (`latestInEntireEnrolment`, `entireEnrolment`, `enrolment`) be used in these rules, producing:

```js
new imports.rulesConfig.RuleCondition({individual}).when.latestValueInEntireEnrolment("")...
```

At runtime `latestValueInEntireEnrolment` does `_getEnrolment(context).findLatestObservationFromEncounters(...)` on an `undefined` enrolment and throws `Cannot read property 'findLatestObservationFromEncounters' of undefined`. Each throw queues a `RuleFailureTelemetry` entity, which is why affected orgs see a sync count that never reaches 0.

## Scope (3 parts)

### 1. Runtime guard — safety net (ship regardless, smallest fix)
- `RuleCondition.latestValueInEntireEnrolment` (`src/rules/RuleCondition.js:169–177`): guard the enrolment so a missing enrolment degrades to "no match" instead of throwing:
```js
const enrolment = this._getEnrolment(context);
const obs = enrolment && enrolment.findLatestObservationFromEncounters(conceptNameOrUuid, context.programEncounter, true, parentConceptNameOrUuid);
```
This is the odd one out — its siblings `latestValueInAllEncounters` / `latestValueInPreviousEncounters` already use `_getIndividualOrEnrolment(context)`. Fixes crashes for **all** existing/hand-written rules fleet-wide (once avni-client bumps the pin).

### 2. Declarative model — carry the enrolment binding
- Extend the LHS / rule JSONB schema (`src/rules/declarative/*`) with, for enrolment-scoped conditions in eligibility rules:
- `enrolmentProgramUuid` (+ name) — which program's enrolment to resolve.
- `enrolmentSelection` — `activeOrRecent` (default) | `any` | `every`.
- rule-level `noEnrolmentFallback` — `eligible` | `notEligible`.
- Fully backward-compatible: absent fields → current behavior.

### 3. Codegen — bind the enrolment + fallback
- `DeclarativeRuleHolder.generateEligibilityRule()` (`src/rules/declarative/DeclarativeRuleHolder.js:42`) currently hardcodes `getAllRuleConditions('individual', true)` → `RuleCondition({individual})`. Emit code that resolves the enrolment for the chosen program, applies the multiplicity strategy, and injects the fallback guard:
```js
const enrolments = individual.nonVoidedEnrolments().filter(e => e.program.uuid === "");
if (enrolments.length === 0) return ;
return enrolments.some(programEnrolment =>
new imports.rulesConfig.RuleCondition({individual, programEnrolment}).when....matches());
```
- `ConceptScope.formTypeToScopeMap` (`src/rules/declarative/ConceptScope.js:4`): add explicit **eligibility** entries. General encounter/enrolment eligibility → only individual-resolvable scopes (registration / latestInAllEncounters / latestInPreviousEncounters / lastEncounter). Program-context eligibility → enrolment scopes allowed **only when paired with a program binding**. (There is no eligibility entry today, which is why the builder reused the `ProgramEncounter` scope list.)

## Acceptance criteria

- [ ] `latestValueInEntireEnrolment` never throws on a missing enrolment (returns "no match").
- [ ] `generateEligibilityRule` binds a resolved `programEnrolment` and injects the not-enrolled fallback for enrolment-scoped conditions.
- [ ] New declarative fields round-trip; rules without them still generate valid code (back-compat).
- [ ] `formTypeToScopeMap` exposes the correct scope set per rule type.
- [ ] Unit tests: runtime guard (enrolment present/absent); codegen (individual-only, single-program, multi-program ANY/EVERY, no-enrolment fallback).
- [ ] Release tagged for avni-webapp and avni-client to pin.

## Related
- Feature / UI: avniproject/avni-webapp#1791
- Consumer bump: avni-client pins `github:openchs/rules-config` and must update to pick up parts 1 & 3.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with src/rules/RuleCondition.js, src/rules/declarative/DeclarativeRuleHolder.js, and src/rules/declarative/ConceptScope.js, then inspect the existing declarative schema and eligibility codegen tests. Verify the runtime guard, enrolment binding and fallback behavior across individual, single-program, and multi-program cases, while preserving round-tripping and backward-compatible generation.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.