Skip to content

Add California foreclosure compliance corpus analyzer - #2227

Open
SamSammane wants to merge 1 commit into
Open-Source-Legal:mainfrom
SamSammane:claude/analysis-combination-xw55db
Open

Add California foreclosure compliance corpus analyzer#2227
SamSammane wants to merge 1 commit into
Open-Source-Legal:mainfrom
SamSammane:claude/analysis-combination-xw55db

Conversation

@SamSammane

Copy link
Copy Markdown

Runs the legalis-ca-foreclosure ruleset (Civ. Code § 2924 et seq.) over a corpus of recorded instruments and stores the compliance chronology on the Analysis.

A foreclosure matter maps onto a corpus: each recorded instrument is a document. This is corpus-scoped rather than per-document because no single instrument answers the question — whether the three-month period under § 2924(a)(2) elapsed needs the Notice of Default and the Notice of Sale together.

The ruleset runs as a sidecar rather than in-process. It is Rust; an FFI boundary would couple this deployment to a Rust toolchain and turn the ruleset's panics into Celery worker crashes.

What is read from documents: instrument type, recording date, date mailed to the trustor, publication and posting dates. What is not read, because it appears on no recorded instrument: the sale date, loan purpose, occupancy, tenders, postponements, payoff requests. Those come from the analyzer input or Corpus.custom_meta, and anything absent reports INSUFFICIENT RECORD rather than being assumed — a finding resting on an assumed fact is worse than no finding.

Instrument classification consults the document title before the body, since a Notice of Default recites the deed of trust it secures and a whole-text scan picks the wrong instrument.

If the service is unreachable the task raises rather than returning "no violations found"; a compliance analyzer that reports a clean bill of health because it could not reach its ruleset is worse than one that crashes, and a test asserts this. The task is idempotent, as CELERY_TASK_ACKS_LATE requires.

Adds the foreclosure-api sidecar behind a compose profile, FORECLOSURE_API_URL and FORECLOSURE_API_TIMEOUT settings, and docs. 32 tests.

Claude-Session: https://claude.ai/code/session_01QmjyGXUNXHKZETogxWuDdu

Runs the legalis-ca-foreclosure ruleset (Civ. Code § 2924 et seq.) over a
corpus of recorded instruments and stores the compliance chronology on the
Analysis.

A foreclosure matter maps onto a corpus: each recorded instrument is a
document. This is corpus-scoped rather than per-document because no single
instrument answers the question — whether the three-month period under
§ 2924(a)(2) elapsed needs the Notice of Default and the Notice of Sale
together.

The ruleset runs as a sidecar rather than in-process. It is Rust; an FFI
boundary would couple this deployment to a Rust toolchain and turn the
ruleset's panics into Celery worker crashes.

What is read from documents: instrument type, recording date, date mailed to
the trustor, publication and posting dates. What is not read, because it
appears on no recorded instrument: the sale date, loan purpose, occupancy,
tenders, postponements, payoff requests. Those come from the analyzer input or
Corpus.custom_meta, and anything absent reports INSUFFICIENT RECORD rather than
being assumed — a finding resting on an assumed fact is worse than no finding.

Instrument classification consults the document title before the body, since a
Notice of Default recites the deed of trust it secures and a whole-text scan
picks the wrong instrument.

If the service is unreachable the task raises rather than returning "no
violations found"; a compliance analyzer that reports a clean bill of health
because it could not reach its ruleset is worse than one that crashes, and a
test asserts this. The task is idempotent, as CELERY_TASK_ACKS_LATE requires.

Adds the foreclosure-api sidecar behind a compose profile, FORECLOSURE_API_URL
and FORECLOSURE_API_TIMEOUT settings, and docs. 32 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QmjyGXUNXHKZETogxWuDdu

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 529b9f02c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

from opencontractserver.documents.models import Document

corpus = Corpus.objects.get(id=corpus_id)
documents = list(Document.objects.filter(corpus=corpus).order_by("id"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Query corpus membership through DocumentPath

Every analyzer invocation fails here because Document has no corpus field; corpus membership is represented by active DocumentPath records. Django therefore raises FieldError before either the empty-corpus handling or evaluation runs. Use the corpus's _get_active_documents() accessor, which applies the required current/non-deleted path predicate.

Useful? React with 👍 / 👎.

Comment on lines +113 to +114
# These dates run at most three tokens ("January 15, 2024").
candidate = " ".join(tail.split()[:3])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse numeric dates without consuming following fields

When a supported one-token date such as Recording Date: 01/15/2024 or 2024-01-15 is followed by another field, tail.split()[:3] appends that field's first two tokens, so parse_date rejects the resulting string and the instrument is dropped for lacking a recording date. This affects normal multiline extracts despite both formats being explicitly listed in _DATE_FORMATS; try the supported token lengths independently or isolate the date value before parsing.

Useful? React with 👍 / 👎.

@JSv4

JSv4 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Hey! Cool contribution, thank you. Will take a look

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants