diff --git a/docs/architecture.md b/docs/architecture.md index ebb1239f..309fe0be 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -105,8 +105,15 @@ owns the only round-1 threshold, and folding the two together would delete a com leaving both names in place. The stages are pinned by `tests/test_round1_preservation.py` against a frozen trace generated -from an independent transcription of the pre-refactor matcher. The PDF path is **not** migrated; -ADR 0020 shares the boundary rule with it, not the implementation. +from an independent transcription of the pre-refactor matcher. + +**The PDF path runs the same four stages, and reached them separately.** It has its own retrieval, +evidence, assignment and classification stages in `diff_pdf.py`, its own byte-identity gate +(`tests/test_pdf_canonical_baseline.py`), and its own boundary tests. What the two paths share is +the *rule* — retrieval controls consideration, assignment controls correspondence — not an +implementation, and ADR 0020 deliberately does not unify them: an XML observation is a parsed tree +node and a PDF observation is a reconstructed block, so a shared matcher would have to pick one +representation and lose the other. ### Why the two paths exist at all diff --git a/docs/decisions/0020-matching-stages.md b/docs/decisions/0020-matching-stages.md index 14e96eac..7107a056 100644 --- a/docs/decisions/0020-matching-stages.md +++ b/docs/decisions/0020-matching-stages.md @@ -245,11 +245,15 @@ the contents of one stage change. - **Let classification re-consult similarity.** Rejected with a carve-out: classification legitimately asks *how much* the corresponding texts differ, which is what `move.body_unchanged` records and needs no score. What it may not do is threshold a - correspondence score. On the XML side that separation now holds: `diff_bill.diff_bills` is - orchestration over the four stages, and the only round-1 cutoff lives in - `apply_similarity_assignment_rule`, an assignment act that runs before classification. - `diff_pdf._hunk_for_paired_blocks` still thresholds inside classification; the PDF track is - not migrated, and this record does not unify the two implementations. + correspondence score. Both pipelines now hold that separation. On the XML side + `diff_bill.diff_bills` is orchestration over the four stages, and the only round-1 cutoff lives + in `apply_similarity_assignment_rule`, an assignment act that runs before classification. On the + PDF side `diff_pdf._hunk_for_paired_blocks` used to call a pair *moved* when the anchors differed + and the bodies cleared `MOVE_THRESHOLD` — a threshold over correspondence evidence, inside + classification. It no longer receives a similarity at all: the rule moved unchanged into + `pdf_round1_move_basis`, an assignment stage, and the function is now the `modified` emitter. + The two pipelines share this boundary rule; they do **not** share an implementation, and this + record does not unify them. - **Keep `Correspondence` pair-shaped, and revisit if consolidation proves common.** Cost asymmetry: capability costs a type permitting N sides, while not having it turns any later change into a migration through every consumer of the matcher's output.