chunk: a reference needs its code, and an act year may be two digits (v0.3.78)

Two divergences from the spec, both reported from a real corpus:

* `art. 360` with no code came back as `art.360`. Every procedural code has
  an art. 360, so an unqualified number is not a citation -- it files three
  different articles under one exact-match key, which is the one thing this
  field exists to prevent. The code suffix is now required.

* `d.p.r. 115/02` extracted as nothing, because the year was pinned to four
  digits. Two to four now, kept verbatim: expanding `02` to `2002` would
  split one act across two keys depending on the source's style.

The spec's prose allowed both readings; it now says which.
Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXRMe9ZPUQCCCtBai9uqt3
parent e3dc237b
......@@ -16,7 +16,7 @@
# Canonical product version for CoderAI — single source of truth. Both the API
# metadata and the admin web UI read from here.
__version__ = "0.3.77"
__version__ = "0.3.78"
__tagline__ = "Complete orchestration, distribution and escalation of remotizable advanced inference."
# Configure the CUDA caching allocator BEFORE torch is imported anywhere.
......
......@@ -364,13 +364,20 @@ def pack(pieces: List[str], target: int, overlap: int) -> List[str]:
# Reference extraction
# ---------------------------------------------------------------------------
_ART_SUFFIX = r"(?:\s*(?:bis|ter|quater|quinquies|sexies))?"
#: The code suffix is MANDATORY, not optional. A bare `art. 360` is ambiguous
#: -- every procedural code has one -- so it is deliberately NOT a reference:
#: this field exists to be exact-matched, and an unqualified number would put
#: three different articles under one key.
_ART_RX = re.compile(
r"\bart(?:t|icol[oi])?\.?\s*(\d+" + _ART_SUFFIX + r")"
r"(?:\s*(?:del\s+|della\s+)?(codciv|c\.?c\.?|c\.?p\.?c\.?|c\.?p\.?p\.?|c\.?p\.?|cost\.?))?",
r"\s*(?:del\s+|della\s+)?(codciv|c\.?c\.?|c\.?p\.?c\.?|c\.?p\.?p\.?|c\.?p\.?|cost\.?)",
re.IGNORECASE)
#: The year is kept VERBATIM, two digits included: `d.p.r. 115/02` normalises
#: to `d.p.r.115/02`, never to `/2002`. Expanding it would silently split the
#: same act across two keys depending on how the source wrote it.
_ACT_RX = re.compile(
r"\b(d\.?\s*lgs\.?|d\.?\s*l\.?|d\.?\s*p\.?\s*r\.?|legge|l\.?)\s*"
r"(?:n\.?\s*)?(\d+)\s*/\s*(\d{4})", re.IGNORECASE)
r"(?:n\.?\s*)?(\d+)\s*/\s*(\d{2,4})", re.IGNORECASE)
_CODE_MAP = {"codciv": "c.c.", "cc": "c.c.", "cpc": "c.p.c.", "cpp": "c.p.p.",
"cp": "c.p.", "cost": "cost."}
_KIND_MAP = {"dlgs": "d.lgs.", "dl": "d.l.", "dpr": "d.p.r.", "legge": "l.",
......
......@@ -251,8 +251,14 @@ extracts article and act citations, normalises them, and de-duplicates while
`bis`/`ter`/`quater`/`quinquies`/`sexies` suffix with spaces removed, and the
code normalises through `codciv|cc → c.c.`, `cpc → c.p.c.`, `cpp → c.p.p.`,
`cp → c.p.`, `cost → cost.`
The code is **required**: a bare `art. 360` is not emitted, because every
procedural code has one and the field exists to be exact-matched — an
unqualified number files three different articles under a single key.
- Acts → `<kind><num>/<year>`, e.g. `d.lgs.209/2005`, with
`dlgs → d.lgs.`, `dl → d.l.`, `dpr → d.p.r.`, `legge|l → l.`
The year is two to four digits and is kept **verbatim**: `d.p.r. 115/02`
normalises to `d.p.r.115/02`, never to `/2002`, so the same act does not
split across two keys depending on how the source wrote it.
These are a filter field and an exact-match signal for hybrid search — the thing
dense embeddings blur, since `art. 2043` and `art. 2054` sit close in vector
......
......@@ -370,6 +370,27 @@ def test_acts_normalise_to_kind_number_year():
assert ch.extract_refs("legge 24/2017") == ["l.24/2017"]
def test_a_bare_article_number_is_not_a_reference():
"""Reported from production: `art. 380` and `art. 360` came back as
`['art.380']` -- an unqualified number is ambiguous (every code has an
art. 360), so it would put three different articles under one exact-match
key. The code suffix is required, and a citation without one is dropped."""
assert ch.extract_refs("si veda l'art. 360 n. 3") == []
assert ch.extract_refs("art.380, art.360, art.115, art.395") == []
# ...but the same numbers WITH a code are still references.
assert ch.extract_refs("violazione dell'art. 380 c.p.c.") == ["art.380 c.p.c."]
def test_an_act_year_may_be_two_digits_and_is_kept_verbatim():
"""`d.p.r. 115/02` was extracted as nothing at all, because the year was
pinned to four digits. It is kept as written: expanding `02` to `2002`
would file the same act under two keys depending on the source's style."""
assert ch.extract_refs("ai sensi del d.p.r. 115/02") == ["d.p.r.115/02"]
assert ch.extract_refs(
"del d.p.r. 115/02 e del d.lgs. 446/1998") == ["d.p.r.115/02",
"d.lgs.446/1998"]
def test_references_are_deduplicated_in_first_seen_order():
txt = "art. 2054 c.c., poi art. 2043 c.c., di nuovo art. 2054 c.c."
assert ch.extract_refs(txt) == ["art.2054 c.c.", "art.2043 c.c."]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment