Deterministic rules engine

The law, computed the same way twice.

South African labour and tax law as executable rules. Reproducible, cited to the section, computed without a language model.

Page four of the Certus Logic audit document for BCEA section 41, showing the statutory text of subsections one to eight beside the executable rule.

The audit document for BCEA s41. Statute on the page, the rule that implements it, and the examples that check it.

Rules in the engine
58
Tests, all passing
616
Statutes in Catala
3
In the compute path
No database.
No network. No model.

A corpus tells you what the law says.
This tells you what to do.

Which form, by when, on which court day, in what amount. That question is arithmetic and lookup, so none of it is generated. Rules are data, counting is code, and both run before any model is called.

Rules as dataappend, never overwrite

One legal value is one flat entry. No switch statement, no hidden default. A change is an append carrying its gazette citation, so a case from 2019 still computes on 2019 law long after the rate moves.

Pure functions onlysame input, same bytes

No database, no network, no Math.random, no Date.now in the compute path. Every result is stamped with engine_version and pack_sha, so you can prove which rules produced a figure months later.

Audit by constructionthe document is the source

The Catala modules print as the audit document. Statute paragraph, the executable rule beside it, worked examples that verify it. The document is not written after the fact. It is the same source the engine runs.

It also refusessilence beats a guess

SCA, High Court and Magistrates' matters are not labour, so the call returns out_of_scope. A provenance gate sits over the claim packs, and a rule that cannot cite its source is never served at all.

Run it yourself

Notice periods,
BCEA s37.

This is the rule itself, not a picture of one. Move the slider and the answer moves with the subsection it comes from.

Land on exactly twelve months and the engine has to break a tie the Act leaves open. It tells you that it did.

4 weeks' notice

The other half: statutory clocks.

Money is one question. The other is how long you have, and courts do not extend a deadline because the arithmetic was hard. The engine carries 58 of these across the CCMA, the Labour Court, the Labour Appeal Court and the Constitutional Court, each with the authority it comes from and what happens when it passes.

CCMA Refer an unfair dismissal LRA s191(1)(b)(i)
You have
30 calendar days
Counted from
The date of dismissal
If it passes
Must apply for condonation under s191(2)

Calendar days. Weekends and public holidays count, and only the last day rolls forward if it falls on a non-working day.

The one that costs cases: a review of a CCMA award runs from the date the award was served, not the date it was made. Those are rarely the same day, and the difference is a case that lapses before anyone opens the file.

Why determinism is worth paying for

Two correct readings of one sentence, nine thousand rand apart, per employee.

BCEA s37(1)(b) says "not more than one year". s37(1)(c)(i) says "one year or more". At exactly twelve months both are true.

Two of our own implementations picked opposite sides and neither was wrong. We found it by running both and comparing. The engine now records which reading it takes and why. An answer you cannot interrogate is not an answer you can defend.

2

weeks under s37(1)(b)

4

weeks under s37(1)(c)(i), the reading we take

R9k

difference per employee at R19,500 a month

Who the arithmetic is expensive for.

The same 58 rules serve anyone who pays for being wrong about South African labour law. Each row below names the functions that already answer it. The first two are where the fit is closest.

Bargaining councils

Statutory councils administer a sector's agreement and run its dispute resolution. They need both halves of the engine, and being quasi-judicial they need an answer that survives being questioned.

resolve, routeDispute, calculateNoticePeriod, checkMinimumWageCompliance

Labour brokers and TES

Deemed employment under LRA s198A, high churn, and every exit needs a notice period, a UIF termination code and a severance check. Volume turns a small error rate into a standing liability.

resolveTerminationCode, calculateSeverancePay, calculateUifBenefit

Debt collection and credit providers

Emoluments attachment orders under s65J of the Magistrates' Courts Act, plus the statutory affordability test. Different regulator from the bank story, and employers processing garnishees get the cap wrong routinely.

checkEaoDeduction, checkBceaDamageDeduction

Corporate advisory, private equity and M&A

Employment liability is a standard due diligence workstream and is usually estimated in a spreadsheet. The s189A facilitation threshold and per head severance are arithmetic, not judgement.

calculateLargeScaleRetrenchmentThreshold, calculateSeverancePay

Trade unions

The mirror image of every calculation here. A union exists in part to check the employer's number, and the same engine answers both sides. The deadline rules matter as much as the money ones.

resolve, calculateCompensationCap, getCondonationGuidance

High headcount sectoral employers

Agriculture, security, cleaning, hospitality, mining. Farm and domestic workers sit on their own notice tier under s37(1)(c)(ii), which is precisely the branch generic payroll software gets wrong.

calculateNoticePeriod, calculateSundayPay, calculatePublicHolidayPay

Labour law practices

Filing specifications, party designations, headings, condonation guidance and appeal checklists across four forums. The clock work a junior does by hand on every matter.

getFilingSpecs, getPartyDesignations, generateAppealChecklist, getForm

Insurers

Legal expense and employment practices liability cover. Compensation caps and deadline risk are actuarial inputs before they are operational ones.

calculateCompensationCap, assessDeadlineRisk

Banks and payroll platforms are covered in detail below. Everything else on this list runs on rules the engine already ships.

Lend against real payroll, not a PDF.

A payslip is a claim. Certus Logic checks the claim. Is the UIF and SDL arithmetic right, was severance computed lawfully, does the affordability schedule survive a retrenchment. One call, the same answer every time, traceable to the gazette.

Affordability and payroll verification

Recompute UIF at one percent each side up to the R17,712 ceiling, SDL at one percent, PAYE brackets and net pay from gross. Catch a mismatch before the credit decision, not after the debit order bounces.

Retrenchment exposure

Price a two hundred person restructuring. Severance under s41, notice under s37, leave pay out, pay in lieu. Versioned by trigger date, so 2025 and 2026 rates never bleed into each other.

Answers as at a past date

A dispute heard in 2026 about a dismissal in 2019 needs 2019 law. Rates are inputs carrying commencement dates, not constants baked into code, so the engine answers as the law stood.

Payroll that knows the Act.

Most payroll software encodes the law once, at build time, and quietly ages. Certus Logic keeps the law as versioned data your platform calls, so a Budget Speech is a data change and not a release.

call

Send gross, period and dates

One HTTPS call carrying facts you already hold. No schema migration, no SDK to adopt, nothing leaves your control beyond the figures in the request.

compute

Rules resolve as at the date

The engine picks the values in force on the date you gave it, applies the rule, and counts court days around holidays and recess where a deadline is involved.

keep

Store the stamp with the record

Each result carries the engine version and rule pack hash. Keep it beside the payslip and that exact figure reproduces years later, on demand.

One endpoint. Every rule.

A single dispatch rather than a route per calculator. Ask what is callable, then call it. Authentication is a header, the response is JSON, and the stamp travels with the answer.

Talk to us

// One dispatch, every function
curl -X POST https://logic.toolkit.zone/v1/call \
  -H "x-api-key: $KEY" \
  -d '{"fn":"calculateNoticePeriod",
      "args":[{"completed_months":12}]}'

{
  "ok": true,
  "notice_weeks": 4,
  "tier_authority": "BCEA 75 of 1997, s37(1)(c)(i)",
  "engine_version": "a3f9c2d"
}

Put the Act in the build.

Tell us the rule you need first and we will show you the call, the citation and the audit document behind it.