avniproject / avniproject/rules-config

Declarative rule support for Approval and Rejection forms

Open
#41 4 comments 0 reactions 1 assignee Claimed by @adamsanadi6 View on GitHub
Dominant language
JavaScript
Stars
0
Forks
3
PR merge metrics
No merged PRs in 30d

Description

**As an** organisation administrator, **I need** the rule builder to work on approval and rejection forms, **so that** I can hide or validate questions without writing JavaScript.

> **This card spans two repositories.** The rule-builder change lands in `rules-config`; the pin bump that makes it reach App Designer lands in `avni-webapp`'s root `package.json`. **Both are this card's work** — the card is not done when the rules-config PR merges. Do not hand the pin bump to `avni-webapp#1805`: that story changes webapp source, this one changes what webapp depends on, and splitting them across two cards is how the dependency gets left behind.

**Merging to `master` does not ship this. The work finishes in two other repos.**

rules-config is not consumed as a branch or a released version. It is pinned as a **git dependency at a specific commit**:

```
"rules-config": "github:avniproject/rules-config#f5f651905ac26dc98b9259245a2c3802bb59f3fe"
```

That pinned commit is a CI-produced build commit (`Build [ci skip] Triggered by 6d29579a8db3`) living outside `master`, so the real flow is: merge to `master` → CI produces a new build commit → **bump the pin in `avni-webapp` on 17.3**. Until the pin moves, nothing reaches a user however the merge is done. rules-config has no 17.3 branch of its own — its release line runs 2.6–14.0 and is numbered independently of the platform repos, which is why the pin, not the branch, is what matters.

**Only `avni-webapp` needs the bump.** It imports `ConceptScope`, `DeclarativeRuleHolder`, `Condition`, `Action`, `RHS`, `Rule` and `CompoundRule` across 10 sites — that is the rule builder, and it is what reads the three maps this story changes. `avni-client` also pins rules-config, but imports exactly one thing from it (`ActionEligibilityResponse`, for edit-form-rule responses, 7 sites) and has **no** references to declarative rules at all: declarative rules are compiled to JavaScript at authoring time in the webapp and stored on the form, and the client simply executes that generated JavaScript through its own rule engine. Bumping the client pin is optional hygiene, not a requirement for this feature.

**Decision: bump the pin to a fresh build. Do not cherry-pick onto the current one.** Verified low-risk — the current pin was built from master commit `6d29579` (6 May 2026), and `master` is **1 commit** ahead of it (`640ecf1`, `avniproject/rules-config#39 | Migrate CircleCI config to v2.1`) with **no changes under `src/`**. So a fresh build carries this story's change and a CI config migration, nothing else.

`openchs/rules-config` and `avniproject/rules-config` are the same repository (id `137749650`); the old org name redirects.

### Acceptance Criteria
- [ ] An administrator building an approval or rejection form can use the rule builder on it, without writing code.
- [ ] The question scopes offered work on every combination these forms can be attached to — a scope that resolves on a visit form must not be offered where it would resolve to nothing on a registration form.
- [ ] The rule builder behaves exactly as before for all existing form types.
- [ ] A rule that hides a question based on the record's registration answers works on a rejection form attached to a visit type. **Depends on `avni-client#2091` supplying the approved subject in `ApprovalFormState.getEntityContext()`** — without it this AC passes review and fails in the field, silently. See Testing Gotchas.
- [ ] **The pin in `avni-webapp` is bumped as part of this card.** The card is done only when the `rules-config` change has merged, CI has produced a new build commit, **and** a change to `avni-webapp`'s root `package.json` on 17.3 points at it. Two merged changes, one card.
- [ ] An administrator using App Designer built from 17.3 with the new pin sees the new form types offered in the rule builder. This is the only check that proves the whole chain worked.
- [ ] Bumping the pin introduces no unrelated behaviour change. *Verify:* the diff between the current pin's source commit `6d29579` and the new build's source is this story's change plus `#39` (CircleCI config), with nothing else under `src/`.

### Technical Details
- **`src/rules/declarative/ConceptScope.js`** — add entries to `formTypeToScopeMap`, which currently covers only the seven classic types.
- **`src/rules/declarative/AddDecisionActionDetails.js:8-13`** and **`src/rules/declarative/VisitScheduleActionDetails.js:7-12`** — add entries.
- Rule variable name is **`entityApprovalStatus`**, matching story 9's `ruleVariableName`.

**In `avni-webapp` (second half of this card):** edit the root `package.json` on 17.3 —
```
"rules-config": "github:avniproject/rules-config#"
```
replacing `f5f651905ac26dc98b9259245a2c3802bb59f3fe`. Take the SHA from the build commit CI produces after the rules-config merge, not from a `master` commit — the pin always points at a build artefact. Commit the lockfile change alongside it.
- **Scope set: use the intersection, not the union.** `formTypeToScopeMap` is keyed by form type alone, but these are the first form types that attach to four different mapping shapes, so a scope valid for an encounter mapping (`lastEncounter`) resolves to nothing on a subject-registration mapping. Offer only what is always resolvable — the approval status itself and `registration`.
- **Decisions and Visit Schedules: empty entries.** Both write to entities the approval does not own, and inventing semantics for them is out of scope. Supporting ConceptScope is what a rejection form actually needs.

**Do NOT:**
- Do **not** copy a scope map from an existing type. Every one of them assumes a single mapping shape.
- Do **not** invent Decision or Visit Schedule behaviour for approval forms.

### Testing Gotchas
- `Task` shipped with **no** declarative rule support and is absent from all three maps, so there is no recent worked example to copy — the seven classic types are the only precedent and they all predate multi-shape form types.
- **The execution path needs no client change, but the registration scope does.** An earlier version of this story said flatly that no client-side change was needed. That is right about *execution* — declarative rules are the no-code builder over the same rule types as JS rules, they run through the generic `RuleEvaluationService.getFormElementsStatuses(entity, entityName, formElementGroup)` path with no per-form-type gate, and `getIndividualUUID` has a `default: return "entity not mapped"` branch rather than throwing.

It is **wrong about the `registration` scope**, which is what the fourth acceptance criterion asks for. An `EntityApprovalStatus` carries only the UUID and type of the record being approved, so the subject cannot be reached from it by navigation the way it can from an encounter or an enrolment. The generated rule binds the subject from `params.entityContext` instead, which means **`avni-client#2091` must have `ApprovalFormState.getEntityContext()` return `{individual: }`**.

Until it does, `AbstractDataEntryState.getEntityContext()` returns `{}`, so `individual` is undefined and `RuleCondition.valueInRegistration`'s guard yields no observation — a registration-scope rule **matches nothing rather than failing**. Silent, so it has to be caught here or in that story, not in QA.
- **Telemetry nit, not a blocker:** because of that `default`, a rule *failure* on these forms records `"entity not mapped"` in place of the individual UUID in `RuleFailure`, making such failures harder to trace back to a subject. Worth knowing when debugging; adding a case to the switch is a small optional improvement, not part of this story.
- Still test end to end on a device rather than only in the builder — the generated JS is what actually runs.
- This repo's version numbering does not track the platform release, so "17.3" means nothing here — the pin is what ties it to a release, not a branch name.
- **The pin bump is the step most likely to be forgotten**, because it lands in a different repo from the one this card is filed against. It is in scope here; nothing else in the epic covers it.
- **Sequencing within the card:** the webapp half cannot start until the rules-config half has merged and CI has produced the build commit. Expect two PRs in two repos with a wait in between — plan the card's time accordingly rather than treating it as one sitting.
- `avni-client` pins the same SHA and spells the org `openchs/rules-config` rather than `avniproject/rules-config`. Same repository (id `137749650`; the old org redirects), and no bump is needed there for this feature.
- The pinned SHA is a build artefact, not a source commit — do not go looking for it in `master`'s history and conclude the pin is broken. Read the `Triggered by ` in its commit message to find the source commit it was built from.

---
**Epic:** https://github.com/avniproject/avni-product/issues/1901

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.