avniproject / avniproject/rules-config
Eligibility rules: guard + bind enrolment for enrolment-scoped conditions (fixes RuleCondition crash)
- 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