Work

LegalDrift: Semantic Version Control for Law

Redlines tell you what words changed; LegalDrift tells you if the substantive meaning changed. By leveraging statistical NLP and embedding-based monitoring, this tool detects semantic drift across legal document revisions. It allows contract managers to verify that renegotiated agreements haven't silently introduced new obligations, treating legal documents with the same observability we apply to software state.

PythonStatistical NLPEmbeddingsAudit LoggingSource ↗
Document v1 / v2
Legal-BERTembeddings
4 statistical tests(KS, MWU, MMD, Energy)
Fisher's methodp-value + severity
Drift report+ audit log

When a software engineer updates code, they use Git diffs to see exactly what changed. When a lawyer updates a contract, they use redlines.

The problem

Redlines have a fatal flaw: they operate at the character and word level. They tell you what words changed, but they cannot tell you if the substantive meaning changed.

A stylistic rewrite of a boilerplate clause will light up a redline document in terrifying red ink, even though the legal effect is identical. Conversely, changing the word "may" to "shall" is a tiny redline blip that completely alters the allocation of risk.

LegalDrift is a statistical monitoring tool for legal document revision, essentially semantic version control for law.

How it works

LegalDrift converts each document into a high-dimensional vector using a language model trained on legal text (Legal-BERT). But it doesn't just do a simple cosine similarity check; it treats the collection of vectors from a document (or chunks of a document) as a statistical distribution, then asks a formal question: are these two distributions the same, or have they diverged?

The four drift testsRun in parallel, then combined into a single p-value via Fisher's method.
TestWhat it measuresWhy it's included
Kolmogorov-SmirnovOverall shape of the cumulative distributionsWorks across all PCA-reduced dimensions
Mann-Whitney ULocation shift (median/mean drift)Robust to outliers
Maximum Mean DiscrepancyKernel-based distributional distancePermutation-based p-values, catches subtle shifts
Energy DistanceGeometric separation between sample cloudsSensitive to distribution shape, not just location

A low combined p-value (typically < 0.05) indicates the two documents occupy measurably different regions of the embedding space. Here's a worked example of how to read the four-test output when a real drift signal is present:

Drift detection: illustrative exampleSynthetic numbers chosen to show how to read the format, not a live run.

Combined p-value fell below the 0.05 threshold.

python sentence_drift_demo.py
Real terminal output: sentence-level drift detection on a synthetic clause revision
A real, reproducible run, not a mockup, comparing two synthetic revisions of a payment clause (notice period 30→90 days, 'may' terminate → 'shall' provide notice, 1%→5% late interest, refund policy reversed) via the package's own DriftDetector at sentence granularity.

No significant drift was detected here (p≈1.0 across all four tests), despite the substantive deontic changes, and that's worth explaining rather than hiding. Two things are going on. The bundled CLI (detect/chunks/compare) embeds each whole document as a single pooled vector, so its statistical tests always compare one sample against one sample: degenerate by construction, independent of input. Running at sentence granularity through the public API fixes that mechanically, but Legal-BERT's pooled sentence embeddings still didn't separate these two clauses by modality (may vs. shall, percentages, day counts); they cluster on lexical/topical similarity more than on deontic force. That's a real, useful finding about where this technique's sensitivity currently ends, not a contradiction of the method.

Localized drift: finding the needle in the haystack

Full-document comparison has a well-known weakness: a large, mostly unchanged 50-page contract can dilute a significant, malicious change inserted into a single clause.

To counter this, LegalDrift supports localized drift detection. It splits documents into semantically coherent chunks (paragraphs, sections, or sentences) and aligns them:

legaldrift chunks contract_v1.txt contract_v2.txt
Chunked Drift Detection Results
============================================================
🔴 [DRIFT] 3. PAYMENT
    p=0.0034, severity=0.2417

🟢 [OK] 1. DEFINITIONS
    p=0.8912, severity=0.0124

🟡 [NEW] 10. AI GOVERNANCE
    added in current document

This tells the human reviewer exactly where to look. You can safely skim the definitions, but you need to heavily scrutinize the payment terms. (The output above is illustrative of the intended UX; see the note above the gif for what a real run against synthetic revisions actually produced.)

Concept extraction without heavy pipelines

Not every task requires the full statistical pipeline. LegalDrift includes a lightweight regex-based extractor that flags common legal concept classes without requiring a heavy NLP pipeline:

  • obligation ("shall", "must")
  • permission ("may", "has the right")
  • prohibition ("shall not", "is prohibited")

This allows for rapid, offline metadata tagging before the heavier embedding tests run.

Audit trails and observability

Legal workflows require strict reproducibility. A core feature of LegalDrift is its DriftHistory module, which persists every detection run to an SQLite database, turning the tool from an ad-hoc CLI script into a persistent audit system that can prove a document was computationally verified for semantic stability on a specific date.

Limitations

A p-value of 0.01 means the embeddings are different. It does not mean the difference is legally material.

  • A shifted comma in a damages cap can be legally catastrophic yet statistically invisible.
  • A stylistic rewrite of a boilerplate clause can be legally trivial yet statistically detectable.
  • Confirmed empirically, not just in theory: the bundled CLI compares whole-document embeddings (n=1 per side), which makes its statistical tests degenerate regardless of input. At sentence granularity the tests run correctly, but in testing, Legal-BERT's pooled embeddings did not reliably separate clauses that differ mainly in deontic modality (obligation vs. permission) rather than topic, exactly the kind of change that matters most legally and least lexically.

LegalDrift is a screening layer, not a verdict. It treats legal documents with the same observability we apply to software state, acting as an early warning system for the humans who make the actual call.

Check out the library on PyPI or contribute on GitHub: OsamaMoftah/LegalDrift.