DocuStory
An analysis engine that turns Korean real-estate documents into risk signals.
- Python
- FastAPI
- Real Estate
Setup
- Context
An experiment to make risk judgement separable into "rules" so the same document yields the same score, regardless of who's reading it.
Build
- QA considerations
- Same document, always the same score - with
nowpinned toFIXED_NOW, does the result exactly match the stored golden? - Does the rule catalog cover the real registry risk signals (mortgage, seizure, provisional seizure) without missing them?
- Was the tradeoff decided - a missed risk (false negative) vs over-flagging (false positive)?
- Are 말소 (cancellation) detection (strike line + text cue) and amount/date normalization robust across varied PDF layouts?
- For scans with no text layer it explicitly refuses (
PARSER_OCR_NOT_IMPLEMENTED), and limits that couldn't be verified are surfaced as PARSER_*/ANALYZER_* warnings, rather than silently returning a wrong answer - Contract schemas across the six repos (ParsedDocument, Rule, AnalysisResult, etc.) are meta-validated by each repo's
test_contractand platform'stest_openapiso contract and implementation can't drift - Rule-catalog integrity, packaging, and deterministic-ordering tests keep the weighted score (warning 15, danger 40, cap 100) reproducible regardless of evaluation order
- Same document, always the same score - with
Outcome
- Metrics
Currently in external validation. Architecture, rule catalog, and implementation details will be published once validation lands.
Process (planning → build)
The same template from problem and hypothesis through scope, spec (SSOT), build, the QA gate, and release - how this was planned and driven.
Process (planning → build)
Problem & hypothesis
A Korean real-estate registry (등기부등본) PDF carries risk signals - mortgages, seizures, provisional seizures - that an ordinary reader cannot reliably decode. The core hypothesis: separating risk judgement into declarative rules makes it regression-testable, so the same document must always yield the same score.
Skills · Problem framing · hypothesis
Scope & priority
Kickoff fixed the scope to the thinnest e2e slice - "one registry PDF → 갑구/을구 extraction → mortgage/seizure detection → a one-line Korean risk summary" (`_roundtable/brief.md`, the agenda doc every repo's CLAUDE.md points to). Registry-only and text-layer-first were core (the parser explicitly rejects image OCR with `PARSER_OCR_NOT_IMPLEMENTED`), while auth, financial/contract document types, and real cloud deploy were deferred.
Skills · Scoping · prioritization
Spec-first (SSOT)
The SSOT was the R2-frozen contract doc (`_roundtable/output/00-r2-decisions.md`) plus per-repo `contracts/*.schema.json` (ParsedDocument, Rule, RuleEvalResult, AnalysisResult, SampleManifest), each owned by exactly one repo. The build then advanced slice by slice, using each repo's `NEXT_BUILD.md` as the plan for the next slice, and allowed schema changes only as backward-compatible extensions - keeping `schema_version` at 0.1.0 across many slices.
Skills · Spec · documentation
Build & iteration
The build split into six repos (samples → parser/rules → analyzer → platform → infra) wired by in-process imports. parser does pdfplumber text extraction plus 갑구/을구 cell mapping, 말소 (cancellation) detection via both a visual strike line and a text cue, and amount/date normalization; rules is a declarative 7-rule JSON catalog with a deterministic evaluator; analyzer computes risk_level, a weighted score (warning 15, danger 40, cap 100), and the Korean summary; platform is a FastAPI POST/GET surface with a CLI and SQLite persistence.
Skills · Execution · delivery
Verification (QA gate)
The QA lens drove verification. analyzer golden tests inject a fixed `now` (FIXED_NOW = 2026-06-05T12:00Z) and assert the produced AnalysisResult exactly matches the stored golden, directly gating the "same document → same score" hypothesis, while platform e2e runs the real parser/rules/analyzer against real sample PDFs and asserts exact risk_level, rule IDs, score, and summary text (e.g. danger_003 sums to 190 contributions and caps to 100 across 6 findings). Under that sit contract-schema meta-validation in every repo (each `test_contract`, platform `test_openapi`, infra manifest schema) plus rule-catalog integrity, packaging, and deterministic-ordering tests, and unverified limits are surfaced honestly through `PARSER_*`/`ANALYZER_*` warnings.
Skills · Quality · risk
Release
It runs via a dev compose and a prod compose (multi-stage, non-root user, healthcheck, named-volume Docker), exposing `POST /v1/documents:analyze` + `GET /v1/documents/{document_id}` plus a single/batch CLI, with SQLite as the default store. It is still MVP - the samples are synthetic PDFs (`gen_synthetic_pdf.py`), so accuracy against real registries is the remaining gate, with auth, an external DB, CI/CD, and a cloud-deploy blueprint left as follow-ups.
Skills · Release
- Tech stack
- Python
- FastAPI