Skip to main content

The compliance rule engine

Alongside the register data layer, Humaniq ships a versioned, static corpus of operative HR/labour and payroll rules — derived from EU labour directives, ILO core conventions, GDPR for employee data, occupational health & safety, national labour law (NL first), and payroll / wage-tax & social-security compliance. An executable RuleEngine audits the register's HR/labour objects against that corpus and reports compliance coverage.

Rules are facts, not config​

Rules are facts about directives and laws — identical for every tenant, changing only with regulation — so they ship as versioned static data, one JSON file per sub-domain under lib/Standards/rules/, not as OpenRegister per-tenant configuration:

  • payroll.json — tax and reporting rules (loonaangifte, UPA pension filing, GL posting, engine traceability)
  • labour.json — labour law (leave, verzuim/Poortwachter, Arbeidstijdenwet, onboarding/offboarding, org-chart and asset integrity, performance dossiervorming)
  • privacy.json — GDPR/AVG rules (recruiting-data retention)

RuleCatalogue (a read-only, versioned accessor, RuleCatalogue::VERSION) loads and merges these files and exposes query helpers. Bump the version on any change to a rule file.

The rule shape​

Every rule declares:

FieldMeaning
idstable slug, unique across the whole corpus
domainsub-domain key (labour, payroll, reporting, gdpr-recruitment, ...)
jurisdictionISO 3166-1 alpha-2, EU, or global
frameworkthe law/framework slug (e.g. bw7-10, nl-poortwachter, nl-arbeidstijdenwet, gdpr)
sourcehuman citation
statementthe operative rule statement
severitymandatory, conditional, or recommended
machineCheckabletrue only when a deterministic program can decide compliance from structured fields

machineCheckable is set true only when compliance is decidable from structured fields — presence, format, arithmetic, enumeration, cardinality, date-window, or referential checks. Narrative or judgemental rules (disclosures, "reasonable", policy text) are false. The audit coverage metric is enforced ÷ machineCheckable, so over-flagging silently deflates coverage — the corpus is expected to flag honestly.

Enforcement: CheckProviders​

A rule becomes enforced when a CheckProvider under lib/Standards/Checks/ registers a predicate keyed by that rule's id. Every CheckProvider implementation is auto-discovered by RuleEngine::providers() — adding a new compliance domain means adding a new provider file, never editing RuleEngine itself. Each predicate is a pure, side-effect-free function fn(array $object, array $context): bool that returns true when the rule is satisfied for that object.

The engine only enforces rules that have both a corpus entry and a registered predicate — the rest of the corpus is catalogued but not yet executable, and grows an executable check per development wave. Every provider referenced throughout this documentation — NlPayrollChecks, NlEngineChecks, NlWageTaxFilingChecks, NlPensionFilingChecks, NlGlPostChecks, NlNetPayChecks, NlLeaveChecks, NlAbsenceChecks, NlAttendanceChecks, NlOnboardingChecks, NlOffboardingChecks, NlAtsChecks, NlPerformanceChecks, NlOrgChecks, NlAssetChecks, NlDocumentChecks, NlSignalChecks, NlFleetChecks, NlDgaChecks, NlWkrChecks, NlWageGarnishmentChecks, NlTravelExpenseChecks, NlRosterChecks, CompChecks — is one of these providers.

The corpus has grown well past its original payroll/labour/privacy scope: it now also covers company-car bijtelling, DGA gebruikelijkloon, werkkostenregeling eindheffing exposure, wage garnishment (loonbeslag), mileage-rate reimbursement, retroactive adjustment consistency, roster ATW pre-checks, and within-band compensation proposals — each backed by one of the providers above, each documented on its own capability page.

Applicability is jurisdiction-scoped: a rule applies to its own country, plus EU-wide rules for EU members and global rules everywhere, so the engine only enforces what an organisation is actually subject to.

Running the audit​

occ humaniq:rules:audit --jurisdiction=NL

The report includes:

Humaniq rule-compliance audit
catalogue version : 2026-07.13
corpus rules : <N> (machine-checkable: <N>)
enforceable today : <N> (<NN.N>% of machine-checkable)

objects checked : <N>
compliant : <N>
with violations : <N>
violations : <N> mandatory / <N> conditional / <N> recommended
<ObjectType> checked=<N> compliant=<N> withViolations=<N>
...

top violated rules:
<rule-id> <N>

Exit code is non-zero for occ humaniq:payroll:verify (the run-scoped payroll audit) on any mandatory violation; the general humaniq:rules:audit command is a read-only reporting tool.

Seeding test data​

occ humaniq:rules:seed-testdata

Idempotently backfills local test data so the enforced rules have something to check — provider sample objects and provider field defaults, applied by RuleTestDataSeeder from each provider's seedSpec().

Where this shows up elsewhere​

Nearly every capability documented elsewhere in this site is backed by one or more corpus rules — see The payroll engine, Retroactive adjustments, Sick pay, Company car bijtelling, DGA payroll mode, Loonbeslag, Werkkostenregeling, Loonaangifte filing, Pension filing, Leave & verzuim, Time & attendance, Rostering, Expense claims, Onboarding & offboarding, Recruiting, Compensation cycles, Performance reviews, Org chart, Documents, Assets, and AVG data-subject rights — for the specific rules each one adds.