DEV Community

Cover image for The Clause Nobody Caught: How I Built Missing-Clause Detection for Contracts
Muaz
Muaz

Posted on

The Clause Nobody Caught: How I Built Missing-Clause Detection for Contracts

Most contract-analysis tools start with the same basic question:

What is wrong with the clauses in this document?

That question is useful, but incomplete. Sometimes the biggest risk is not a badly written clause. It is a clause that does not exist.

I ran into this while building AuditGuard, an AI-assisted compliance analysis tool. A client used it to review an agreement before signing. The system flagged risky language, cited potentially relevant requirements, and suggested draft replacements for human review.

The finding that changed the conversation, however, came from a different section of the report: Missing Clauses.

The agreement appeared not to address a provision that could be required under the selected compliance framework. There was no suspicious sentence for a reviewer to highlight because the relevant language was absent.

The client did not treat that output as a legal conclusion. He used it as a focused question to raise during the contract review and postponed signing until the issue had been addressed.

That case captures an interesting engineering problem: How do you search for text that is not there?

Why ordinary clause analysis misses this

A conventional contract-analysis pipeline usually looks like this:

  1. Split the document into clauses.
  2. Classify each clause by topic.
  3. Retrieve regulations or policies related to its text.
  4. Ask a model whether the clause creates a potential issue.
  5. Generate an explanation and possible remediation.

This pipeline can identify problematic wording. It cannot reliably identify an omitted requirement because retrieval begins with the document's existing text.

If a contract says nothing about breach notification, for example, there may be no breach-notification language to retrieve the corresponding requirement.

Missing-clause detection has to reverse the direction of the search:

Instead of asking which requirements match each clause, ask whether every applicable required provision is covered by any clause.

The two-stage approach

I implemented gap analysis as a separate pass after clause extraction. It combines a deterministic retrieval stage with a constrained model review.

At a high level, the process is:

contract
  -> extract clause inventory
  -> load required provisions for selected frameworks
  -> score each provision against every clause
  -> shortlist provisions with weak or no coverage
  -> ask a model to verify the shortlist against the clause inventory
  -> report only high-confidence, apparently unaddressed provisions
Enter fullscreen mode Exit fullscreen mode

The split matters. Comparing every regulation with every clause using a large model would be slow, expensive, and difficult to control. Pure similarity search would be cheaper, but it would produce too many false positives.

Stage 1: deterministic candidate selection

The first stage uses TF-IDF similarity as a fast coverage screen.

For every required provision, the system records its best similarity score across all extracted clauses:

for clause in clauses:
    for provision, score in search(clause.text):
        coverage[provision.id] = max(
            coverage.get(provision.id, 0),
            score,
        )
Enter fullscreen mode Exit fullscreen mode

A required, high-impact provision becomes a gap candidate when no clause reaches the configured coverage threshold.

candidates = [
    provision
    for provision in required_provisions
    if coverage.get(provision.id, 0) < COVERAGE_THRESHOLD
]
Enter fullscreen mode Exit fullscreen mode

This is deliberately only a screening step. Low lexical similarity does not prove that a provision is missing. Contracts often express the same obligation using different vocabulary.

To keep the next stage bounded, candidates are prioritized by severity and low coverage, then capped per framework.

Stage 2: conservative model verification

The second stage gives the model two things:

  • an inventory of the contract's clauses; and
  • the shortlisted required provisions.

For each provision, the model must return structured data:

{
  "regulation_ref": "...",
  "addressed": false,
  "confidence": 0.91,
  "rationale": "No clause appears to address ...",
  "matched_clause": null
}
Enter fullscreen mode Exit fullscreen mode

The prompt is intentionally conservative. A provision counts as addressed when any clause covers its subject matter, even partially or with different wording. The model is also told to judge coverage—not final legal sufficiency.

The system reports a candidate only when the model says it is unaddressed and its confidence clears a minimum threshold.

This second pass filters cases where TF-IDF missed a semantic match. It also produces a short rationale that a human reviewer can verify.

Why this is not a legal conclusion

An important distinction is easy to lose in product copy:

“The system did not find coverage” is not the same as “the contract violates the law.”

Whether a provision is actually required depends on facts outside the text, including jurisdiction, the parties' roles, the data involved, and the purpose of the agreement. A model can also miss indirect coverage or misunderstand a cross-reference.

That is why the output should be presented as a review queue, not a verdict. In AuditGuard, the finding includes the source reference, rationale, confidence, and suggested draft language. The user still needs to verify applicability and wording, and should involve qualified counsel when the decision carries legal risk.

What I learned

Three design decisions made the feature more useful.

1. Treat absence detection as its own retrieval problem

Clause-by-clause analysis and gap analysis answer opposite questions. Trying to handle both in one prompt makes the logic harder to inspect and test.

2. Use models for verification, not exhaustive search

Deterministic retrieval reduces the search space. The model then handles the narrower semantic question that similarity scoring cannot answer reliably.

3. Optimize for reviewability

A missing-clause warning without a citation or rationale is difficult to trust. Each finding should tell the reviewer what may be missing, why it was selected, and which source requirement triggered it.

The broader pattern

This approach is not limited to contracts. The same pattern can help find missing controls in security policies, absent sections in technical specifications, or unaddressed requirements in procurement responses:

  1. Build an inventory of what exists.
  2. Define the set of requirements expected to be covered.
  3. Cheaply shortlist weakly covered requirements.
  4. Semantically verify those candidates.
  5. Send uncertain results to a human.

Finding problematic text is classification. Finding missing text is coverage analysis. Treating them as separate problems makes the retrieval logic easier to test, bounds model usage, and keeps the output reviewable.


Disclosure: I built AuditGuard, the product discussed in this article. AuditGuard provides AI-assisted compliance analysis for informational purposes and does not provide legal advice. I used AI to help edit this article and reviewed its technical claims against the implementation before publication.

Top comments (0)