Eligibility and qualification
An application must be tested against stated criteria. The same facts must always get the same answer, a declined application must be explained by the criterion it failed, and the cases a person should look at must reach a person. This example decides who qualifies for a water utility's hardship payment plan.
How this runs
Every result on this page is the recorded run of the rulebook below through the Helixor rule engine, case by case. In production a rulebook like this runs in a digital worker Available that you build and deploy in the console or with the agent command-line tools; console access is granted on request (email hello@helixor.ai). The rule language itself is described in Rules.
The policy#
- Criteria every household must meet.
An active service account, at least one month in arrears, income no more than 10 percent over the program limit, and no default on a payment plan in the last 12 months. Failing any one declines the application, and the household is told which.
- Cases a person decides.
Income up to 10 percent over the limit is borderline: a case worker reviews it. Income that is not yet verified holds the application until the documents arrive.
- What follows.
A scheduled disconnection is paused while an application that is not declined is open.
- Missing facts.
Nothing is decided until the five facts the criteria read are all known.
What the rules decided#
Each row is one case run through the rulebook. The outcome is one of four; the deciding rule is the one whose recommendation won; derived facts show every criterion the rules established; and the last column lists the obligations and prohibitions that follow.
| Case | Outcome | Deciding rule | Derived facts | What follows |
|---|---|---|---|---|
| S1 Three months behind, income well under the limit, documents verified. | APPROVE | RDEC04: A complete application that meets every criterion and needs no review qualifies. | QUALIFIES | none |
| S2 Income 25 percent over the program limit. | DECLINE | RDEC01: A household that fails any criterion is declined. | FAILS_INCOME INELIGIBLE | OBLIGATION_SEND_ELIGIBILITY_NOTICE |
| S3 Income 6 percent over the limit: inside the review band. | HOLD | RDEC02: Borderline income, with no failed criterion, is held for a case worker. | BORDERLINE_INCOME REVIEW_RECOMMENDED | OBLIGATION_ASSIGN_CASE_WORKER |
| S4 Qualifies on every criterion, but income is not yet verified. | HOLD | RDEC03: Unverified income, with no failed criterion, is held until the documents arrive. | INCOME_UNVERIFIED DOCUMENTS_REQUESTED | OBLIGATION_REQUEST_INCOME_DOCUMENTS |
| S5 Defaulted on a payment plan in the last 12 months. | DECLINE | RDEC01: A household that fails any criterion is declined. | FAILS_PLAN_HISTORY INELIGIBLE | OBLIGATION_SEND_ELIGIBILITY_NOTICE |
| S6 The account is up to date. | DECLINE | RDEC01: A household that fails any criterion is declined. | FAILS_ARREARS INELIGIBLE | OBLIGATION_SEND_ELIGIBILITY_NOTICE |
| S7 Qualifies, and a disconnection is already scheduled. | APPROVE | RDEC04: A complete application that meets every criterion and needs no review qualifies. | QUALIFIES | OBLIGATION_PAUSE_DISCONNECTION |
| S8 Income against the limit has not been provided. | NEED_DATA | No rule ran: missing income_pct_of_limit | none | none |
| Outcome | Means here |
|---|---|
APPROVE | The household qualifies for the plan. |
DECLINE | A criterion failed; the derived facts name which one (FAILS_INCOME, FAILS_ARREARS and so on). |
HOLD | A person decides: a case worker for borderline income, or the application waits for income documents. |
NEED_DATA | A fact the criteria read is missing; the result names it. |
Two things to notice. A declined case carries the failed criterion as a derived fact, so the notice can name it without anyone rereading the file. And the borderline band is a rule of its own: moving it is a one-line change, reviewed like any other.
The rulebook#
Every rule names its owner, its authority, how hard it is, and what it means in plain English, and binds to a typed vocabulary so a misspelled fact fails at load instead of silently never matching.
# Example hardship payment plan eligibility rulebook in the Helixor rule language. # Fictional domain for helixor.dev: a water utility decides who qualifies for a hardship payment plan. # Classes and predicates: ontology.json. Each case fact <name> reaches the rules as HAS_<NAME>, # carrying its value, and KNOWN_<NAME>, true when it has one. # Recommendations are resolved by @authority, then @priority, then decline > hold > approve. # ---------------------------------------------------------------- required data @domain utility_assistance @ontology example_hardship_plans @ontology_ver 1 @authority policy @owner assistance-programs @severity hard @category data @priority 100 @required_fact_mode explicit @required_facts [HAS_ACCOUNT_ACTIVE, HAS_MONTHS_IN_ARREARS, HAS_INCOME_PCT_OF_LIMIT, HAS_INCOME_VERIFIED, HAS_PRIOR_PLAN_DEFAULT] @english The application is complete when account status, arrears, income against the limit, income verification and plan history are all known. @uses_predicates [hasAccountActive, hasMonthsInArrears, hasIncomePctOfLimit, hasIncomeVerified, hasPriorPlanDefault] @uses_classes [ServiceAccount, Household, PlanApplication] RDAT01: IF KNOWN_ACCOUNT_ACTIVE AND KNOWN_MONTHS_IN_ARREARS AND KNOWN_INCOME_PCT_OF_LIMIT AND KNOWN_INCOME_VERIFIED AND KNOWN_PRIOR_PLAN_DEFAULT THEN FILE_COMPLETE # ---------------------------------------------------------------- criteria that fail @domain utility_assistance @ontology example_hardship_plans @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @required_fact_mode explicit @required_facts [HAS_ACCOUNT_ACTIVE] @english Only an active service account can be placed on a plan. @uses_predicates [hasAccountActive] @uses_classes [ServiceAccount] RCR01: IF FILE_COMPLETE AND HAS_ACCOUNT_ACTIVE == 0 THEN FAILS_ACTIVE_ACCOUNT @domain utility_assistance @ontology example_hardship_plans @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @required_fact_mode explicit @required_facts [HAS_MONTHS_IN_ARREARS] @english A plan is for an account at least one month in arrears. @uses_predicates [hasMonthsInArrears] @uses_classes [ServiceAccount] RCR02: IF FILE_COMPLETE AND HAS_MONTHS_IN_ARREARS < 1 THEN FAILS_ARREARS @domain utility_assistance @ontology example_hardship_plans @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @required_fact_mode explicit @required_facts [HAS_INCOME_PCT_OF_LIMIT] @english Household income more than 10 percent over the program limit does not qualify. @uses_predicates [hasIncomePctOfLimit] @uses_classes [Household] RCR03: IF FILE_COMPLETE AND HAS_INCOME_PCT_OF_LIMIT > 1.10 THEN FAILS_INCOME @domain utility_assistance @ontology example_hardship_plans @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @required_fact_mode explicit @required_facts [HAS_PRIOR_PLAN_DEFAULT] @english A household that defaulted on a payment plan in the last 12 months does not qualify. @uses_predicates [hasPriorPlanDefault] @uses_classes [Household] RCR04: IF FILE_COMPLETE AND HAS_PRIOR_PLAN_DEFAULT == 1 THEN FAILS_PLAN_HISTORY @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @english An inactive account makes the household ineligible. RCR05: IF FAILS_ACTIVE_ACCOUNT THEN INELIGIBLE @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @english No arrears makes the household ineligible. RCR06: IF FAILS_ARREARS THEN INELIGIBLE @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @english Income over the limit makes the household ineligible. RCR07: IF FAILS_INCOME THEN INELIGIBLE @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category criterion @priority 100 @english A recent plan default makes the household ineligible. RCR08: IF FAILS_PLAN_HISTORY THEN INELIGIBLE # ---------------------------------------------------------------- cases a person decides @domain utility_assistance @ontology example_hardship_plans @authority policy @owner assistance-programs @severity soft @category borderline @priority 100 @required_fact_mode explicit @required_facts [HAS_INCOME_PCT_OF_LIMIT] @english Income up to 10 percent over the limit is borderline: a case worker reviews it. @uses_predicates [hasIncomePctOfLimit] @uses_classes [Household] RBL01: IF FILE_COMPLETE AND HAS_INCOME_PCT_OF_LIMIT > 1.0 AND HAS_INCOME_PCT_OF_LIMIT <= 1.10 THEN BORDERLINE_INCOME @domain utility_assistance @ontology example_hardship_plans @authority policy @owner assistance-programs @severity soft @category verification @priority 100 @required_fact_mode explicit @required_facts [HAS_INCOME_VERIFIED] @english Income that is not yet verified must be documented before a plan starts. @uses_predicates [hasIncomeVerified] @uses_classes [PlanApplication] RBL02: IF FILE_COMPLETE AND HAS_INCOME_VERIFIED == 0 THEN INCOME_UNVERIFIED # ---------------------------------------------------------------- recommendations @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category decision @priority 100 @english A household that fails any criterion is declined. RDEC01: IF INELIGIBLE THEN RECOMMEND_DECLINE @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category decision @priority 80 @english Borderline income, with no failed criterion, is held for a case worker. RDEC02: IF BORDERLINE_INCOME AND NOT INELIGIBLE THEN RECOMMEND_HOLD AND REVIEW_RECOMMENDED @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category decision @priority 80 @english Unverified income, with no failed criterion, is held until the documents arrive. RDEC03: IF INCOME_UNVERIFIED AND NOT INELIGIBLE THEN RECOMMEND_HOLD AND DOCUMENTS_REQUESTED @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category decision @priority 100 @english A complete application that meets every criterion and needs no review qualifies. RDEC04: IF FILE_COMPLETE AND NOT INELIGIBLE AND NOT BORDERLINE_INCOME AND NOT INCOME_UNVERIFIED THEN RECOMMEND_APPROVE AND QUALIFIES # ---------------------------------------------------------------- what follows from the decision @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category obligation @priority 100 @english A declined household is told which criterion it did not meet. ROBL01: IF RECOMMEND_DECLINE THEN OBLIGATION(send_eligibility_notice) @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category obligation @priority 100 @english A held application for unverified income triggers a request for income documents. ROBL02: IF DOCUMENTS_REQUESTED THEN OBLIGATION(request_income_documents) @domain utility_assistance @authority policy @owner assistance-programs @severity hard @category obligation @priority 100 @english A borderline case is assigned to a case worker. ROBL03: IF REVIEW_RECOMMENDED THEN OBLIGATION(assign_case_worker) @domain utility_assistance @ontology example_hardship_plans @authority policy @owner assistance-programs @severity hard @category obligation @priority 100 @required_fact_mode explicit @required_facts [HAS_DISCONNECTION_SCHEDULED] @english A scheduled disconnection is paused while an application that is not declined is open. @uses_predicates [hasDisconnectionScheduled] @uses_classes [ServiceAccount] ROBL04: IF HAS_DISCONNECTION_SCHEDULED == 1 AND NOT INELIGIBLE THEN OBLIGATION(pause_disconnection)
The full recorded run
ontology example_hardship_plans v1: 3 classes, 6 properties rulebook hardship-plan@v1: 19 rules, bound to ontology example_hardship_plans: yes == S1-qualifies: Three months behind, income well under the limit, documents verified. facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=0 outcome: APPROVE report: fired; fired 2 of 19 rules fired: RDAT01 RDEC04 derived facts: FILE_COMPLETE QUALIFIES recommendations: RECOMMEND_APPROVE obligations: none deciding rule RDEC04 (policy, hard, priority 100): A complete application that meets every criterion and needs no review qualifies. resolution: only_candidate: RDEC04 recommends APPROVE == S2-over-income: Income 25 percent over the program limit. facts: account_active=1, months_in_arrears=3, income_pct_of_limit=1.25, income_verified=1, prior_plan_default=0 outcome: DECLINE report: fired; fired 5 of 19 rules fired: RDAT01 RCR03 RCR07 RDEC01 ROBL01 derived facts: FILE_COMPLETE FAILS_INCOME INELIGIBLE recommendations: RECOMMEND_DECLINE obligations: OBLIGATION_SEND_ELIGIBILITY_NOTICE deciding rule RDEC01 (policy, hard, priority 100): A household that fails any criterion is declined. resolution: only_candidate: RDEC01 recommends DECLINE == S3-borderline-income: Income 6 percent over the limit: inside the review band. facts: account_active=1, months_in_arrears=3, income_pct_of_limit=1.06, income_verified=1, prior_plan_default=0 outcome: HOLD report: fired; fired 4 of 19 rules fired: RDAT01 RBL01 RDEC02 ROBL03 derived facts: FILE_COMPLETE BORDERLINE_INCOME REVIEW_RECOMMENDED recommendations: RECOMMEND_HOLD obligations: OBLIGATION_ASSIGN_CASE_WORKER deciding rule RDEC02 (policy, hard, priority 80): Borderline income, with no failed criterion, is held for a case worker. resolution: only_candidate: RDEC02 recommends HOLD == S4-documents-pending: Qualifies on every criterion, but income is not yet verified. facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=0, prior_plan_default=0 outcome: HOLD report: fired; fired 4 of 19 rules fired: RDAT01 RBL02 RDEC03 ROBL02 derived facts: FILE_COMPLETE INCOME_UNVERIFIED DOCUMENTS_REQUESTED recommendations: RECOMMEND_HOLD obligations: OBLIGATION_REQUEST_INCOME_DOCUMENTS deciding rule RDEC03 (policy, hard, priority 80): Unverified income, with no failed criterion, is held until the documents arrive. resolution: only_candidate: RDEC03 recommends HOLD == S5-recent-default: Defaulted on a payment plan in the last 12 months. facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=1 outcome: DECLINE report: fired; fired 5 of 19 rules fired: RDAT01 RCR04 RCR08 RDEC01 ROBL01 derived facts: FILE_COMPLETE FAILS_PLAN_HISTORY INELIGIBLE recommendations: RECOMMEND_DECLINE obligations: OBLIGATION_SEND_ELIGIBILITY_NOTICE deciding rule RDEC01 (policy, hard, priority 100): A household that fails any criterion is declined. resolution: only_candidate: RDEC01 recommends DECLINE == S6-no-arrears: The account is up to date. facts: account_active=1, months_in_arrears=0, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=0 outcome: DECLINE report: fired; fired 5 of 19 rules fired: RDAT01 RCR02 RCR06 RDEC01 ROBL01 derived facts: FILE_COMPLETE FAILS_ARREARS INELIGIBLE recommendations: RECOMMEND_DECLINE obligations: OBLIGATION_SEND_ELIGIBILITY_NOTICE deciding rule RDEC01 (policy, hard, priority 100): A household that fails any criterion is declined. resolution: only_candidate: RDEC01 recommends DECLINE == S7-disconnection-scheduled: Qualifies, and a disconnection is already scheduled. facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=0, disconnection_scheduled=1 outcome: APPROVE report: fired; fired 3 of 19 rules fired: RDAT01 RDEC04 ROBL04 derived facts: FILE_COMPLETE QUALIFIES recommendations: RECOMMEND_APPROVE obligations: OBLIGATION_PAUSE_DISCONNECTION deciding rule RDEC04 (policy, hard, priority 100): A complete application that meets every criterion and needs no review qualifies. resolution: only_candidate: RDEC04 recommends APPROVE == S8-missing-income: Income against the limit has not been provided. facts: account_active=1, months_in_arrears=3, income_verified=1, prior_plan_default=0 outcome: NEED_DATA missing facts: income_pct_of_limit rulebook not run: the decision type's required facts are missing
Adapt it to your policy#
- Replace the criteria with yours, one rule each, so every decline names exactly one criterion.
- Keep borderline bands as separate rules that hold rather than decline: they are where a person adds the most.
- Give the decision type the facts every criterion reads as required facts, so a missing fact asks for data instead of deciding.