Skip to content

LLM-Assisted Regulation Development

AI-generated payroll regulations and tests with human review

Large language models can generate Payroll Engine regulations, test cases and examples from natural language requirements. The PE's declarative JSON/YAML architecture is structurally well-suited for this: regulations are well-constrained documents with no logic outside expressions and scripting hooks — exactly the format LLMs produce reliably.

The automated test framework closes the loop: generated output is verified by the test runner without manual case-by-case review. This concentrates human effort on what requires actual payroll domain knowledge.

The Development Model

Phase Who What
1. Define requirements Human Natural language description of the regulation
2. Generate regulation LLM Cases, WageTypes, Collectors, Lookups in JSON/YAML
3. Generate tests LLM Expected results for test employees
4. Review & complete Human Apply domain knowledge, close gaps
5. Verify PE Test Runner Automated verification of all test results

Phases 2 and 3 typically cover 90–95% of the work. Phase 4 takes 30–60 minutes for a regulation of moderate complexity — compared to 1–2 days for a full manual build.

Practical Example: GlobalTech Payroll

The GlobalPayroll example was designed as a reference for this workflow. Prompt.md in that folder contains the natural language requirements used as input, with the human corrections documented alongside.

Phase 1 — Requirements (Human → LLM)

"Create a Payroll Engine regulation for GlobalTech, a fictitious technology company.

The following rules apply: - Base salary = gross salary × employment level - Employees aged 50 or older receive a senior supplement of 5% on the base salary - A one-time performance bonus is paid in the month it is recorded - Progressive income tax: up to 3 000 → 10%, up to 6 000 → 18%, up to 10 000 → 25%, above → 32% - Social security: 10% (national), health insurance: 5% (company)

Two test employees: - Sarah Chen, born 1985, full-time, salary 6 000, January bonus 1 000 - Carlos Rodriguez, born 1970, 60% part-time, salary 10 000 (full-time basis)"*

Phase 2 — Generate Regulation (LLM)

The LLM generates the complete Payroll.json. Key elements:

Cases:

"cases": [
  { "name": "SocialSecurity",   "caseType": "National",
    "fields": [{ "name": "SocialSecurityRate",  "valueType": "Percent", "timeType": "Period" }] },
  { "name": "HealthInsurance",  "caseType": "Company",
    "fields": [{ "name": "HealthInsuranceRate", "valueType": "Percent", "timeType": "CalendarPeriod" }] },
  { "name": "Salary",           "caseType": "Employee",
    "fields": [{ "name": "Salary",              "valueType": "Money",   "timeType": "CalendarPeriod" }] },
  { "name": "EmploymentLevel",  "caseType": "Employee",
    "fields": [{ "name": "EmploymentLevel",     "valueType": "Percent", "timeType": "Period" }] },
  { "name": "BirthDate",        "caseType": "Employee",
    "fields": [{ "name": "BirthDate",           "valueType": "Date",    "timeType": "Timeless" }] },
  { "name": "PerformanceBonus", "caseType": "Employee",
    "fields": [{ "name": "PerformanceBonus",    "valueType": "Money",   "timeType": "Moment" }] }
]

Lookup: IncomeTax (progressive bracket):

"lookups": [
  { "name": "IncomeTax", "rangeSize": 0.01,
    "values": [
      { "key": "Bracket1", "rangeValue": 1,     "value": "\"0.10\"" },
      { "key": "Bracket2", "rangeValue": 3000,  "value": "\"0.18\"" },
      { "key": "Bracket3", "rangeValue": 6000,  "value": "\"0.25\"" },
      { "key": "Bracket4", "rangeValue": 10000, "value": "\"0.32\"" }
    ]
  }
]

Wage Types:

"wageTypes": [
  { "wageTypeNumber": 100, "name": "BaseSalary",
    "valueExpression": "^^Salary * ^^EmploymentLevel",
    "collectors": ["GrossIncome"] },
  { "wageTypeNumber": 110, "name": "SeniorSupplement",
    "valueExpression": "Age(^^BirthDate) >= 50 ? WageType[100] * 0.05M : 0M",
    "collectors": ["GrossIncome"] },
  { "wageTypeNumber": 120, "name": "PerformanceBonus",
    "valueExpression": "^^PerformanceBonus",
    "collectors": ["GrossIncome"] },
  { "wageTypeNumber": 200, "name": "IncomeTax",
    "valueExpression": "var rate = GetRangeLookup<decimal>(\"IncomeTax\", Collector[\"GrossIncome\"]); return rate != null ? Collector[\"GrossIncome\"] * rate : 0M;",
    "collectors": ["Deductions"] },
  { "wageTypeNumber": 210, "name": "SocialSecurity",
    "valueExpression": "Collector[\"GrossIncome\"] * CaseValue[\"SocialSecurityRate\"]",
    "collectors": ["Deductions"] },
  { "wageTypeNumber": 220, "name": "HealthInsurance",
    "valueExpression": "Collector[\"GrossIncome\"] * CaseValue[\"HealthInsuranceRate\"]",
    "collectors": ["Deductions"] }
]

Phase 3 — Generate Tests (LLM)

The LLM calculates expected results and generates Test.et.json. Two periods are covered:

January 2024 — Sarah Chen (full-time, bonus 1 000):

Wage Type Calculation Result
100 BaseSalary 6 000 × 1.0 6 000.00
110 SeniorSupplement age 38 < 50 0.00
120 PerformanceBonus Moment Jan 2024 1 000.00
GrossIncome 7 000.00
200 IncomeTax 7 000 × 25 % (Bracket3) 1 750.00
210 SocialSecurity 7 000 × 10 % 700.00
220 HealthInsurance 7 000 × 5 % 350.00
Deductions 2 800.00

February 2024 — Sarah Chen (no bonus — Moment validation):

Wage Type Calculation Result
120 PerformanceBonus Moment not in Feb — must be 0 0.00
GrossIncome 6 000.00
Deductions 2 400.00

The February result is the critical verification: it confirms that the Moment time type correctly scopes the bonus to a single period.

Phase 4 — Review & Complete (Human)

Check Typical LLM issue Human correction
Tax bracket Boundary logic (≤ vs. <) Verify rangeSize and bracket values
Senior supplement Age reference date Confirm Age() is evaluated at period start
Moment bonus Aggregation behaviour Clarify PeriodAggregation for multiple entries
Retroactive behaviour Usually not generated Add retro test case for employment level change
Naming conventions Spaces, special characters Ensure PascalCase for all object names

Phase 5 — Verify (PE Test Runner)

Test.pecmd

The test runner executes the payrun and compares all wageTypeResults and collectorResults against expected values. Every deviation is reported as a failure — no manual review required.

What LLMs Do Well

Task Quality
Derive cases from requirements ✅ Very good
WageType expressions (No-Code) ✅ Very good
Structure lookup tables ✅ Very good
Calculate expected test results ✅ Good (simple arithmetic)
Collector assignments ✅ Good
Progressive tax / bracket logic ⚠️ Review boundary cases
Retroactive scenarios ⚠️ Rarely correct without guidance
Time overlaps and splitting ❌ Domain knowledge required
Compliance-specific rules ❌ Human verification mandatory

See Also