The short answer.
Every night a script reads the Lebanese University's pages again, rebuilds the data and opens a pull request. A person used to read it and merge it. Now a job does: it waits for the checks, reviews the data and merges. If a rule fails it writes the reasons on the pull request, leaves it open and tells us.
The five rules.
Only files under data/ and db/model/ may change. Every check must finish without failing. No table may lose more than 1% of its rows, with 50 rows always allowed. No more than 25 laws, or 25 rulings, may disappear. No more than 5% of page dates may move.
Why these rules.
Each one comes from a night that went wrong. On 3 November a trial run built from an incomplete copy of the source pages would have removed 93,487 laws. The same run would have reset 164,305 of 165,855 page dates. And in October an outage at the source looked like 3,000 laws withdrawn. Run against that trial data, the review refuses it.
It merges what it reviewed.
The merge names the exact commit that was reviewed, so a later push to the same branch cannot be merged unseen. Right before merging, the job reads that commit's file list again and refuses anything outside data/ and db/model/. On the first night's real pull request it printed what a person had found by hand, two decrees removed and five dates moved, in 0.3 seconds.
Answers a model gave are not asked twice.
The build asks a language model about amendments and citations it has not judged before. On a night whose pull request was still open, those answers lived only on its branch, and the next night asked again: paid again, and possibly answered differently. The nightly now carries them forward for every question the main branch has no answer to.
The morning it did not run.
On 5 November the 06:40 run stopped after 7 seconds. The night before had left its export in the working folder, the same files had since been merged, and git refused to update over them. The script now puts the folder back as it found it when it ends, and a test fails if it does not.