lawdangle-kr 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lawdangle_kr-0.1.0/.gitignore +15 -0
- lawdangle_kr-0.1.0/DESIGN.md +254 -0
- lawdangle_kr-0.1.0/LICENSE +21 -0
- lawdangle_kr-0.1.0/PKG-INFO +189 -0
- lawdangle_kr-0.1.0/README.ko.md +175 -0
- lawdangle_kr-0.1.0/README.md +175 -0
- lawdangle_kr-0.1.0/examples/analyze_xlsx.py +119 -0
- lawdangle_kr-0.1.0/examples/probe_api.py +63 -0
- lawdangle_kr-0.1.0/examples/sample_corpus.txt +6 -0
- lawdangle_kr-0.1.0/examples/webapp.py +299 -0
- lawdangle_kr-0.1.0/pyproject.toml +40 -0
- lawdangle_kr-0.1.0/src/lawdangle/__init__.py +51 -0
- lawdangle_kr-0.1.0/src/lawdangle/classifier.py +202 -0
- lawdangle_kr-0.1.0/src/lawdangle/cli.py +196 -0
- lawdangle_kr-0.1.0/src/lawdangle/mapper.py +286 -0
- lawdangle_kr-0.1.0/src/lawdangle/models.py +100 -0
- lawdangle_kr-0.1.0/src/lawdangle/parser.py +87 -0
- lawdangle_kr-0.1.0/src/lawdangle/report.py +86 -0
- lawdangle_kr-0.1.0/src/lawdangle/resolver.py +575 -0
- lawdangle_kr-0.1.0/test/fixtures/deunggi.json +19 -0
- lawdangle_kr-0.1.0/test/fixtures/gongyusumyeon.json +19 -0
- lawdangle_kr-0.1.0/test/test_classifier.py +148 -0
- lawdangle_kr-0.1.0/test/test_live_api.py +145 -0
- lawdangle_kr-0.1.0/test/test_mapper.py +96 -0
- lawdangle_kr-0.1.0/test/test_parser.py +38 -0
- lawdangle_kr-0.1.0/test/test_regression.py +52 -0
- lawdangle_kr-0.1.0/test/test_resolver.py +49 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# lawdangle-kr — 설계 문서
|
|
2
|
+
|
|
3
|
+
> **lawdangle-kr** — Dead Cross-Reference Detector for Korean Statutes
|
|
4
|
+
> 현행 법령이 인용하는 대상 법령의 **폐지·개명·이관·사문화**를 탐지하고 5분류로 태깅한다.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 네이밍
|
|
9
|
+
|
|
10
|
+
| 용도 | 이름 | 비고 |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| PyPI 배포명 / 레포명 | **`lawdangle-kr`** | 스코프(한국 법령 전용)를 정직하게 표기. 법제처 API·한국 입법체계(타법개정·전부개정·폐지제정)에 종속되므로 `-kr` 명시 |
|
|
13
|
+
| import 모듈명 | **`lawdangle`** | 코드 안에서는 짧게 — `from lawdangle import ...` |
|
|
14
|
+
| CLI 명령 | **`lawdangle`** | `lawdangle <법령ID 또는 파일>` |
|
|
15
|
+
|
|
16
|
+
- **어원:** law + *dangling reference* — 포인터/링크가 죽은 대상을 가리키는 기술 용어. 개발자에게 "법령판 dangling reference 검출기"로 즉시 전달됨.
|
|
17
|
+
- **`-kr`를 붙이는 이유:** 다른 나라 법령 도구로 오해/충돌 방지 + 향후 코어(`lawdangle`) + 국가별 어댑터(`-kr`) 확장 여지.
|
|
18
|
+
- **피한 형태:** `krlawdangle`(검색 불리), `lawdanglekr`(kr이 안 보임), `lawcheck`(너무 일반적, 아류로 보임).
|
|
19
|
+
- **확정 전 체크:** PyPI에 `lawdangle-kr` 비어 있는지 확인 후 선점.
|
|
20
|
+
- 참고: pip는 배포명의 하이픈/언더스코어를 동일 취급(`lawdangle-kr` ↔ `lawdangle_kr`)하지만 import는 불가 → **배포명 `lawdangle-kr` / import명 `lawdangle`** 분리가 깔끔.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 0. 한 줄 정의 (README 최상단에 그대로 쓸 문장)
|
|
25
|
+
|
|
26
|
+
기존 인용 검증 도구는 "인용된 조문이 **실존하느냐**"까지만 본다.
|
|
27
|
+
이 도구는 "인용된 법령이 **아직 살아있는 좌표를 가리키느냐**"를 본다.
|
|
28
|
+
— 즉, 폐지·개명·이관·사문화된 죽은 참조(dangling reference)를 잡는다.
|
|
29
|
+
|
|
30
|
+
### 기존 도구와의 경계 (반박 차단용)
|
|
31
|
+
|
|
32
|
+
| | 검증하는 것 | 예시 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| 실존 검증 (예: verify_citations) | 인용하는 **쪽** 조문이 진짜냐 | "형법 제9999조" → 없음 |
|
|
35
|
+
| **이 도구 (liveness)** | 인용당하는 **대상**이 폐지/개명/이관됐냐 | "「국가균형발전 특별법」 제17조제2항" → 폐지·삭제됨 |
|
|
36
|
+
|
|
37
|
+
방향이 반대다. 전자는 텍스트 안의 가짜를 잡고, 후자는 텍스트 안의 죽은 참조를 잡는다.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. 문제 정의
|
|
42
|
+
|
|
43
|
+
현행 법령(시행령 포함)의 본문은 다른 법령을 `「법령명」 제○조제○항제○호` 형태로 인용한다.
|
|
44
|
+
그런데 인용 대상 법령이 이후 개명·폐지·이관되어도, 인용하는 쪽 본문은 자동으로 갱신되지 않는다(타법개정 누락).
|
|
45
|
+
그 결과 살아있는 법이 죽은/낡은 좌표를 가리키는 상태가 발생한다.
|
|
46
|
+
|
|
47
|
+
**핵심 함정:** 법명 단순 치환(string replace)으로 정규화하면, 분할 이관·조문번호 변경 케이스에서 **틀린 곳을 더 틀린 곳으로** 보내는 오정정이 발생한다.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. 5분류 (도구의 심장)
|
|
52
|
+
|
|
53
|
+
판정 축 두 개:
|
|
54
|
+
- **축1 — 대상 존재:** 인용 대상 제도/조문이 현행 체계에 존재하는가
|
|
55
|
+
- **축2 — 좌표 일치:** 인용에 적힌 (법명 + 조문번호)가 현행과 일치하는가
|
|
56
|
+
|
|
57
|
+
| 코드 | 유형 | 대상 존재 | 좌표 일치 | 작동 | 정비 방법 | 심각성 |
|
|
58
|
+
|---|---|---|---|---|---|---|
|
|
59
|
+
| **A** | 단순 개명 | O | 법명 X / 조문 O | O (해석규칙) | 법명만 교체 | 5 (낮음) |
|
|
60
|
+
| **B** | 전부개정·이관 (조문 대응) | O | 법명 X / 조문 X | O (승계규칙) | 법명+조문 교체 | 4 |
|
|
61
|
+
| **C** | 분할 승계 | O (1:N) | 법명 X / 조문 X | O | 호 단위 수동 매핑 | 2 (높음) |
|
|
62
|
+
| **D** | 사문화 (전제 소멸) | X | — | 보통 자체조문으로 작동 | 인용부 삭제·재구성 | 3 |
|
|
63
|
+
| **E** | 순수 폐지 (빈 참조) | X | — | X 가능 | 입법 보완 | 1 (최상) |
|
|
64
|
+
|
|
65
|
+
### 심각성 순위 (제보/정비 우선순위)
|
|
66
|
+
`E > C > D > B > A`
|
|
67
|
+
판정 기준: "수범자가 본문만으로 권리·의무를 확정할 수 있는가" + "정정 난이도".
|
|
68
|
+
임팩트는 **E·C·D**에 있다. A·B는 양은 많아도 단순 행정 정비.
|
|
69
|
+
|
|
70
|
+
### 검증된 실증 케이스 (정답지 / 회귀 테스트 고정값)
|
|
71
|
+
|
|
72
|
+
| 입력 | 인용 | 정답 | 근거 |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| 공유수면법 §13①14 | 「국가균형발전 특별법」 §17② | **B** | 제도는 「지역 산업위기 대응 및 지역경제 회복을 위한 특별법」 §10로 이관. 조문번호 §17②→§10 변경. 단순 법명치환 시 오정정(지방분권균형발전법 §17은 무관 조항) |
|
|
75
|
+
| 등기특별회계법 §3 | 「국유재산관리특별회계법」 §6 | **D** | 특별회계 자체가 폐지(2007.1.1)되어 일반회계로 흡수. "그 규정에 불구하고" 특례의 전제가 소멸. 세입·세출은 자체 각 호로 작동 |
|
|
76
|
+
|
|
77
|
+
> 이 2건은 `test/fixtures/`에 박아넣고, 출력이 B/D로 안 나오면 회귀 실패로 처리한다.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 3. 파이프라인 (4단계)
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
[법령 텍스트]
|
|
85
|
+
│
|
|
86
|
+
▼ ① 인용 추출 (Parser)
|
|
87
|
+
[ {citing_law, citing_article, cited_law_name, cited_article}, ... ]
|
|
88
|
+
│
|
|
89
|
+
▼ ② 연혁 조회 (History Resolver) — 법제처 API
|
|
90
|
+
[ + {cited_law_status, successor_info, ...} ]
|
|
91
|
+
│
|
|
92
|
+
▼ ③ 분류 (Classifier) — 5분류 + 신뢰도
|
|
93
|
+
[ + {category: A~E, confidence, flag} ]
|
|
94
|
+
│
|
|
95
|
+
▼ ④ 리포트 (Reporter)
|
|
96
|
+
[CSV / JSON]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 단계별 책임
|
|
100
|
+
|
|
101
|
+
#### ① Parser — 인용 추출
|
|
102
|
+
- 입력: 법령 본문 문자열(또는 법령 ID로 본문 fetch).
|
|
103
|
+
- 추출 단위: `「(법령명)」\s*(제\d+조(의\d+)?(\s*제\d+항)?(\s*제\d+호)?)`
|
|
104
|
+
- 출력: 인용 레코드 리스트. 한 조문에 여러 인용 가능.
|
|
105
|
+
- 주의:
|
|
106
|
+
- 「」 안에 법령명이 아닌 게 들어오는 경우(고시명, 별표명) 필터.
|
|
107
|
+
- 조문번호 한글표기(제5조제2항) ↔ API 코드(000500...) 변환 필요. (korean-law-mcp의 변환 로직 참고 가능)
|
|
108
|
+
- 약칭(화관법 등)은 풀네임 정규화 후 조회.
|
|
109
|
+
|
|
110
|
+
#### ② History Resolver — 연혁 조회
|
|
111
|
+
- 입력: `cited_law_name`.
|
|
112
|
+
- 법제처 OPEN API로 해당 법령의 **현재 상태**를 조회.
|
|
113
|
+
- 분기에 필요한 신호 (※ 실제 필드명은 API 응답 확인 후 채울 것):
|
|
114
|
+
- `제정·개정구분` 또는 `법령상태` → 현행 / 폐지 / 타법폐지 `// TODO: 필드명`
|
|
115
|
+
- 폐지인 경우: **후속·대체법 정보**가 응답/연혁에 있는가 `// TODO: 후속법 필드 유무 확인`
|
|
116
|
+
- 개명인 경우: 옛 명칭 ↔ 현행 명칭 매핑 `// TODO`
|
|
117
|
+
- 캐시: 같은 법령 반복 조회 많음 → law명 기준 캐시(TTL 24h 권장).
|
|
118
|
+
|
|
119
|
+
> ⚠️ 설계 급소: A vs (B/C/D/E)를 가르는 1차 분기가 이 필드에서 나온다.
|
|
120
|
+
> 폐지법의 후속법 정보가 API로 **단일하게** 떨어지면 B 자동판정 가능,
|
|
121
|
+
> 후속법이 **없으면** D/E 후보, **여럿이면** C 후보. 이 떨어지는 형태를 먼저 확인할 것.
|
|
122
|
+
|
|
123
|
+
#### ③ Classifier — 5분류
|
|
124
|
+
- 입력: 인용 레코드 + 연혁 정보.
|
|
125
|
+
- 자동 판정 가능 범위 / 수동 플래그 경계를 **명확히** 둔다.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
판정 의사결정 트리:
|
|
129
|
+
|
|
130
|
+
1. cited_law 현행 유지?
|
|
131
|
+
├─ YES → 인용 법명이 현행명과 동일?
|
|
132
|
+
│ ├─ YES → 정상 (분류 대상 아님)
|
|
133
|
+
│ └─ NO → 약칭/표기차이 확인 후, 실제 개명이면 A
|
|
134
|
+
└─ NO (개명/폐지)
|
|
135
|
+
│
|
|
136
|
+
2. 개명(법인격 동일, 내용 연속)?
|
|
137
|
+
│ └─ YES → A
|
|
138
|
+
│
|
|
139
|
+
3. 폐지 → 후속법 정보 분석
|
|
140
|
+
├─ 후속법 1개 + 인용 조문에 대응 조문 확정 가능 → B
|
|
141
|
+
├─ 후속법 여러 개(내용 분산) → C [FLAG: 수동 — 호 단위 매핑 필요]
|
|
142
|
+
├─ 후속법 없음 + 인용 조항이 자체 완결(대상 없어도 결론 남) → D [FLAG: 수동 확인]
|
|
143
|
+
└─ 후속법 없음 + 인용 효과가 적용 불능 → E [FLAG: 수동 — 최우선 검토]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- **자동/수동 경계 (정직하게 둘 것):**
|
|
147
|
+
- 자동 확정: A (개명 확인), B (후속법 단일 + 조문 대응 명확)
|
|
148
|
+
- **수동 플래그 필수: C, D, E** — 이 셋은 "대상 제도가 어디로 갔는지 / 자체 완결인지 / 효과가 죽었는지"를 사람이 봐야 정확. 자동으로 단정하면 오탐.
|
|
149
|
+
- 각 결과에 `confidence: high|medium|low|manual_required` 부여.
|
|
150
|
+
- C/D/E를 억지로 자동 확정하지 않는 것이 이 도구의 신뢰도 핵심. (원본 시트가 "대체법 있으면 직접 찾아 메모"로 비워둔 것과 같은 이유)
|
|
151
|
+
|
|
152
|
+
#### ④ Reporter — 출력
|
|
153
|
+
- CSV 컬럼(권장):
|
|
154
|
+
`citing_law, citing_article, cited_law_name, cited_article, cited_status, category(A~E), confidence, successor_suggestion, flag, note`
|
|
155
|
+
- 집계 요약: 전체 N건 중 A/B/C/D/E 분포 + 심각성 상위(E,C,D) 건수.
|
|
156
|
+
- 이 집계표가 곧 어필/제보/정비제안의 핵심 산출물.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 4. 모듈 구조 (Python 독립 레포 기준)
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
lawdangle-kr/
|
|
164
|
+
├── README.md # §0 한 줄 정의 + 실증 2케이스 + 1줄 실행법
|
|
165
|
+
├── pyproject.toml # name="lawdangle-kr", pip install 가능하게
|
|
166
|
+
├── src/
|
|
167
|
+
│ └── lawdangle/
|
|
168
|
+
│ ├── __init__.py
|
|
169
|
+
│ ├── parser.py # ① 인용 추출
|
|
170
|
+
│ ├── resolver.py # ② 법제처 API 연혁 조회 + 캐시
|
|
171
|
+
│ ├── classifier.py # ③ 5분류 (도구의 심장)
|
|
172
|
+
│ ├── report.py # ④ CSV/JSON 출력
|
|
173
|
+
│ ├── models.py # 데이터클래스: Citation, HistoryInfo, Result
|
|
174
|
+
│ └── cli.py # `lawdangle <법령ID 또는 파일>` 한 줄 실행
|
|
175
|
+
├── test/
|
|
176
|
+
│ └── fixtures/
|
|
177
|
+
│ ├── gongyusumyeon.json # 공유수면법 → 정답 B
|
|
178
|
+
│ └── deunggi.json # 등기특별회계법 → 정답 D
|
|
179
|
+
└── examples/
|
|
180
|
+
└── sample_corpus.txt
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 데이터 모델 (models.py 스케치)
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from dataclasses import dataclass
|
|
187
|
+
from enum import Enum
|
|
188
|
+
|
|
189
|
+
class Category(Enum):
|
|
190
|
+
A = "rename" # 단순 개명
|
|
191
|
+
B = "transfer_mapped" # 이관, 조문 대응
|
|
192
|
+
C = "split_succession" # 분할 승계
|
|
193
|
+
D = "obsolete" # 사문화
|
|
194
|
+
E = "dangling" # 순수 폐지(빈 참조)
|
|
195
|
+
|
|
196
|
+
class Confidence(Enum):
|
|
197
|
+
HIGH = "high"
|
|
198
|
+
MEDIUM = "medium"
|
|
199
|
+
LOW = "low"
|
|
200
|
+
MANUAL = "manual_required"
|
|
201
|
+
|
|
202
|
+
@dataclass
|
|
203
|
+
class Citation:
|
|
204
|
+
citing_law: str
|
|
205
|
+
citing_article: str
|
|
206
|
+
cited_law_name: str
|
|
207
|
+
cited_article: str | None
|
|
208
|
+
|
|
209
|
+
@dataclass
|
|
210
|
+
class HistoryInfo:
|
|
211
|
+
status: str # 현행 / 폐지 / 타법폐지 ... // TODO: API 매핑
|
|
212
|
+
current_name: str | None # 개명 시 현행명
|
|
213
|
+
successors: list[str] # 후속·대체법 (0개=D/E후보, 1개=B후보, N개=C후보)
|
|
214
|
+
# ... API 응답 보고 확장
|
|
215
|
+
|
|
216
|
+
@dataclass
|
|
217
|
+
class Result:
|
|
218
|
+
citation: Citation
|
|
219
|
+
history: HistoryInfo
|
|
220
|
+
category: Category | None
|
|
221
|
+
confidence: Confidence
|
|
222
|
+
successor_suggestion: str | None
|
|
223
|
+
note: str
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 5. 구현 순서 (권장)
|
|
229
|
+
|
|
230
|
+
1. **models.py** — 자료구조부터 고정. 나머지가 여기 맞춰 붙는다.
|
|
231
|
+
2. **resolver.py** — 폐지법(국유재산관리특별회계법) 1건으로 API 응답 받아 **필드 매핑 확정**. ← 여기가 막히면 전체가 막힘. 제일 먼저.
|
|
232
|
+
3. **classifier.py** — 의사결정 트리 구현. 단, C/D/E는 플래그만 달고 자동 단정 금지.
|
|
233
|
+
4. **parser.py** — 정규식 + 조문번호 변환.
|
|
234
|
+
5. **report.py + cli.py** — CSV 떨구고 한 줄 실행.
|
|
235
|
+
6. **test/fixtures** — 공유수면법(B)·등기특별회계법(D) 회귀 고정.
|
|
236
|
+
|
|
237
|
+
> 2번이 전체의 급소. resolver가 폐지/개명/후속법을 어떤 필드로 떨어뜨리는지 확정되면 나머지는 기계적.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 6. 스코프 경계 (안 할 것 / 함정)
|
|
242
|
+
|
|
243
|
+
- **안 할 것:** 자동 정정(법명 치환) 기능을 기본값으로 넣지 말 것. 이 도구는 "탐지·분류"까지. 자동 치환은 C/B에서 오정정을 낳으므로, 정정은 "제안(suggestion)"으로만, 적용은 사람이.
|
|
244
|
+
- **함정 1:** 약칭/표기 차이(가운뎃점 ㆍ, 띄어쓰기)를 개명으로 오판 → 정규화 후 비교.
|
|
245
|
+
- **함정 2:** 후속법 단일이라고 무조건 B 아님 — 조문 대응이 깨지면(조문번호 변경) A로 처리하면 틀림. B로 가되 조문 교체 필요 플래그.
|
|
246
|
+
- **함정 3:** "폐지=흠결"로 단정 금지. D는 작동하고, B도 작동한다. 효력 마비는 E(+일부 D)만. 리포트에서 이 톤을 유지해야 반박당하지 않음.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 7. 공개 전략 (요약)
|
|
251
|
+
|
|
252
|
+
- **공개:** 도구(코드) + 검증된 2케이스 + 5분류 기준.
|
|
253
|
+
- **비공개 유지:** 원본 6,479건 전체 분류표(자산). 도구가 있으면 재생성 가능하므로 원본을 통째로 풀 이유 없음. 공개하려면 검증 완료 후 "정제 데이터셋"으로 별도.
|
|
254
|
+
- **연결:** korean-law-mcp 레포에 이슈 — "실존 검증 너머 liveness(폐지/이관) 검증" 차별점 + 본 레포 링크.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yabooung
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lawdangle-kr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dead cross-reference detector for Korean statutes
|
|
5
|
+
Project-URL: Homepage, https://github.com/yabooung/lawdangle-kr
|
|
6
|
+
Author: yabooung
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: requests
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# lawdangle-kr
|
|
16
|
+
|
|
17
|
+
> **Dead Cross-Reference Detector for Korean Statutes**
|
|
18
|
+
> Detects and classifies references in in-force Korean laws that point to **repealed, renamed, transferred, or obsolete** target laws.
|
|
19
|
+
|
|
20
|
+
[](https://www.python.org/)
|
|
21
|
+
[](LICENSE)
|
|
22
|
+
|
|
23
|
+
**한국어 README → [README.ko.md](README.ko.md)**
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
Existing citation-checkers verify only whether *the citing article itself exists*.
|
|
28
|
+
lawdangle asks a different question: **does the cited law still point at a living coordinate?** — that is, it catches *dangling references* to laws that have been repealed, renamed, transferred, or hollowed out.
|
|
29
|
+
|
|
30
|
+
| | Checks | Example |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| Existence check | Whether the **citing** article is real | "Criminal Act art. 9999" → does not exist |
|
|
33
|
+
| **lawdangle (liveness)** | Whether the **cited target** was repealed / renamed / transferred | "「National Balanced Development Special Act」 art. 17(2)" → repealed & transferred |
|
|
34
|
+
|
|
35
|
+
The direction is reversed: the former catches fakes inside the text; the latter catches dead references inside the text.
|
|
36
|
+
|
|
37
|
+
> **The core trap:** naive string-replacement of law names produces *mis-corrections* in split-transfer and article-renumbering cases — sending a wrong reference somewhere *more* wrong. lawdangle therefore **detects and proposes**, but never auto-applies corrections.
|
|
38
|
+
|
|
39
|
+
## The five categories
|
|
40
|
+
|
|
41
|
+
| Code | Type | Works? | Remedy | Severity |
|
|
42
|
+
|---|---|---|---|---|
|
|
43
|
+
| **A** | Simple rename | Yes | Replace name only | 5 (low) |
|
|
44
|
+
| **B** | Full amendment / transfer (article mapped) | Yes | Replace name + article | 4 |
|
|
45
|
+
| **C** | Split succession (1 → N) | Yes | Manual item-level mapping | 2 (high) |
|
|
46
|
+
| **D** | Obsolete (premise dissolved) | Usually self-contained | Delete / restructure the citation | 3 |
|
|
47
|
+
| **E** | Pure repeal (empty reference) | May break | Legislative fix | 1 (highest) |
|
|
48
|
+
|
|
49
|
+
Severity ranking (remediation priority): **E > C > D > B > A**.
|
|
50
|
+
**C / D / E are never auto-asserted** — they are flagged for human review with evidence attached. This restraint is the heart of the tool's reliability.
|
|
51
|
+
|
|
52
|
+
### Automatic classification scope (live API)
|
|
53
|
+
|
|
54
|
+
| Verdict | How | Confidence |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| In-force / Repealed | `target=law` · `eflaw` lookup | Auto (high) |
|
|
57
|
+
| **A — Rename** | **Law-ID continuity** (old name absent, same ID alive under a new name) + **cited-article survival check** | Auto (high) |
|
|
58
|
+
| **A — Official abbreviation** | Official abbreviation dictionary (`lsAbrv`, ~2,600 entries) → full name | Auto (high) |
|
|
59
|
+
| **A — Truncated name** | Conservative `endswith` partial match → full name | Auto (medium) |
|
|
60
|
+
| B — Rename + article moved | Renamed, but the cited article is deleted/renumbered in the current text → suggests `--map` | Auto candidate (medium) |
|
|
61
|
+
| B — Transfer / C — Split | Repeal + successor candidates (addendum back-trace + amendment-reason) → article mapping is manual | Flag + candidates |
|
|
62
|
+
| D — Obsolete / E — Repeal | Repeal + absorption signal (general account) / no successor | Flag |
|
|
63
|
+
| Informal abbreviation (not in dictionary) | Exact match fails → UNKNOWN (mis-match avoided on purpose) | — |
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install lawdangle-kr
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Requires Python 3.10+. Live lookups need a free **법제처(MOLEG) OPEN API** key (OC), issued at <https://open.law.go.kr>.
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# RECOMMENDED — analyze a whole statute by name (fetches the in-force body itself).
|
|
77
|
+
# No copy-paste, no case-law contamination, and the citing article is filled in.
|
|
78
|
+
LAW_OC=your_oc lawdangle --law "공유수면 관리 및 매립에 관한 법률" --format csv
|
|
79
|
+
|
|
80
|
+
# Analyze pasted statute text
|
|
81
|
+
LAW_OC=your_oc lawdangle path/to/law.txt --format summary
|
|
82
|
+
|
|
83
|
+
# Offline demo (history fixtures — no API key needed)
|
|
84
|
+
lawdangle examples/sample_corpus.txt \
|
|
85
|
+
--fixture test/fixtures/gongyusumyeon.json test/fixtures/deunggi.json \
|
|
86
|
+
--format csv
|
|
87
|
+
|
|
88
|
+
# --deep: also map the concrete corresponding article for B / rename+moved cases
|
|
89
|
+
LAW_OC=your_oc lawdangle path/to/law.txt --format csv --deep
|
|
90
|
+
|
|
91
|
+
# Article-correspondence helper (old article → successor article)
|
|
92
|
+
# Successor auto-discovered when omitted (works for split transfers too):
|
|
93
|
+
LAW_OC=your_oc lawdangle --map "국가균형발전 특별법" "제17조제2항"
|
|
94
|
+
# ...or specify the successor explicitly:
|
|
95
|
+
LAW_OC=your_oc lawdangle --map "국가균형발전 특별법" "제17조제2항" \
|
|
96
|
+
"지역 산업위기 대응 및 지역경제 회복을 위한 특별법"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from lawdangle import run_law
|
|
101
|
+
from lawdangle.resolver import LawGoKrResolver
|
|
102
|
+
|
|
103
|
+
resolver = LawGoKrResolver("your_oc")
|
|
104
|
+
for r in run_law("공유수면 관리 및 매립에 관한 법률", resolver):
|
|
105
|
+
if r.category: # skip live/normal references
|
|
106
|
+
print(r.citation.citing_article, r.citation.cited_law_name,
|
|
107
|
+
r.citation.cited_article, "→", r.category.name, "|", r.note)
|
|
108
|
+
|
|
109
|
+
# Or work at the citation level directly:
|
|
110
|
+
from lawdangle import parse_citations, classify
|
|
111
|
+
for c in parse_citations(open("law.txt", encoding="utf-8").read()):
|
|
112
|
+
res = classify(c, resolver.resolve(c.cited_law_name))
|
|
113
|
+
print(res.category, res.note)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Pipeline
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
[statute text]
|
|
120
|
+
① Parser extract 「law」 + article citations
|
|
121
|
+
② Resolver MOLEG API — current status, rename, successors (cached)
|
|
122
|
+
③ Classifier 5-way decision tree (+ confidence, manual flag)
|
|
123
|
+
④ Reporter CSV / JSON / summary
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## How successor detection works (live)
|
|
127
|
+
|
|
128
|
+
The search API has no "successor law" field, but the **repealed-law detail** (`lawService.do`) does. lawdangle back-traces successors via two routes:
|
|
129
|
+
|
|
130
|
+
- **Repeal-by-other-law back-trace** — when a law was repealed by another law's *addendum* (`타법폐지`), the addendum header `Addendum(<the repealing law>) … repeals 「self」` names the **repealing law = the successor**.
|
|
131
|
+
*e.g.* 저탄소 녹색성장 기본법 → 「기후위기 대응을 위한 탄소중립ㆍ녹색성장 기본법」; 국토이용관리법 → 「국토의 계획 및 이용에 관한 법률」.
|
|
132
|
+
- **Amendment-reason prose** — extracts 「successor law」 names from the enactment/amendment reason (filtering out the addendum's "amendments to other laws", which are collateral, and keeping only law-shaped names).
|
|
133
|
+
- **Absorption signal** (`absorbed`) — phrases like "…merged into the general account" mean *no successor law* → a strong **D** signal.
|
|
134
|
+
|
|
135
|
+
Candidate count drives the class: **1 → B (transfer)**, **2+ → C (split)**, **0 + absorption → D**. All are flagged for manual confirmation.
|
|
136
|
+
|
|
137
|
+
### Content-based successor discovery (split transfers)
|
|
138
|
+
|
|
139
|
+
The back-traced candidate is the *law's general successor* — which often differs from the *specific provision's* successor (the split-transfer trap). For example, 국가균형발전 특별법's general successor is 「지방자치분권 및 지역균형발전에 관한 특별법」, but its art. 17(2) 산업위기대응특별지역 scheme actually moved to a **different** law: 「지역 산업위기 대응 및 지역경제 회복을 위한 특별법」.
|
|
140
|
+
|
|
141
|
+
lawdangle finds that automatically: it takes the **distinctive concept terms** from the old article's text (e.g. *산업위기대응특별지역*) and searches current **law names** for them — the law that *defines* the scheme surfaces (usually a single hit), and article mapping follows. So `--map` works even when you do **not** supply the successor, and `--deep` attaches the discovered successor + article to B/C results.
|
|
142
|
+
|
|
143
|
+
## Article correspondence (`--map`, `--deep`)
|
|
144
|
+
|
|
145
|
+
For transfers (B), `--map` narrows "old §17(2) → which successor article?". It walks the old law's history to find the **last substantive text before deletion** (a cited article may have been deleted *before* the law itself was repealed), then ranks the successor's current articles by **text similarity**. When the citation has a paragraph (②), it further narrows **down to the paragraph** inside the top article (item-level stays manual — that is category C).
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
$ lawdangle --map "국가균형발전 특별법" "제17조제2항" "지역 산업위기 대응 및 지역경제 회복을 위한 특별법"
|
|
149
|
+
|
|
150
|
+
옛 법령 : 「국가균형발전 특별법」 제17조 (substantive text from the 2022-01-13 version)
|
|
151
|
+
후속법령: 「지역 산업위기 대응 및 지역경제 회복을 위한 특별법」
|
|
152
|
+
판정 : manual — multiple close candidates (possible split) (paragraph: §1, sim 0.205)
|
|
153
|
+
후보(by similarity):
|
|
154
|
+
1. 제10조제1항 (0.18) ① the Minister … upon receiving a designation application …
|
|
155
|
+
2. 제12조 (0.176) …
|
|
156
|
+
3. 제9조 (0.172) …
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
It surfaces the design's known answer **§17(2) → art. 10** as the #1 candidate (제10조제1항), while *not* auto-confirming (arts. 9/10/12 are close — possible split) and leaving the final call to a human. That is precisely how the "mis-correction" trap is avoided.
|
|
160
|
+
|
|
161
|
+
## Validated cases (regression fixtures)
|
|
162
|
+
|
|
163
|
+
| Input | Citation | Answer |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| 공유수면법 §13①14 | 「국가균형발전 특별법」 §17② | **B** — institution transferred to 「지역 산업위기 대응 및 지역경제 회복을 위한 특별법」 |
|
|
166
|
+
| 등기특별회계법 §3 | 「국유재산관리특별회계법」 §6 | **D** — special account abolished & absorbed into the general account; the citing article still works on its own |
|
|
167
|
+
|
|
168
|
+
These two are pinned in `test/fixtures/`; if the output is not B / D, the regression fails.
|
|
169
|
+
|
|
170
|
+
## Scope (what it deliberately does *not* do)
|
|
171
|
+
|
|
172
|
+
- It **detects and classifies** only. Automatic name-substitution is not a default — corrections are emitted as *suggestions*; a human applies them. (Auto-substitution causes mis-corrections in B/C cases.)
|
|
173
|
+
- It does **not** equate "repealed" with "defective". D works, B works; only E (and some D) actually breaks enforceability. The report keeps this tone so its findings hold up.
|
|
174
|
+
- Informal abbreviations not in the official dictionary resolve to UNKNOWN rather than risk a wrong match.
|
|
175
|
+
|
|
176
|
+
## Development
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
pip install -e ".[dev]"
|
|
180
|
+
pytest # offline tests run anywhere; live API tests run only when an OC key is present
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Live tests read the OC key from `LAW_OC` or a local `.env` (`oc=...`); without it they are skipped.
|
|
184
|
+
|
|
185
|
+
See [DESIGN.md](DESIGN.md) for the full design rationale.
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# lawdangle-kr
|
|
2
|
+
|
|
3
|
+
> **현행 법령의 죽은 인용(dead cross-reference) 검출기**
|
|
4
|
+
> 현행 법령이 인용하는 대상 법령의 **폐지·개명·이관·사문화**를 탐지하고 5분류로 태깅한다.
|
|
5
|
+
|
|
6
|
+
[](https://www.python.org/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
**English README → [README.md](README.md)**
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
기존 인용 검증 도구는 "인용된 조문이 **실존하느냐**"까지만 본다.
|
|
14
|
+
lawdangle은 다른 질문을 던진다 — "인용된 법령이 **아직 살아있는 좌표를 가리키느냐?**" 즉, 폐지·개명·이관·사문화된 **죽은 참조(dangling reference)** 를 잡는다.
|
|
15
|
+
|
|
16
|
+
| | 검증하는 것 | 예시 |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| 실존 검증 | 인용하는 **쪽** 조문이 진짜냐 | "형법 제9999조" → 없음 |
|
|
19
|
+
| **lawdangle (liveness)** | 인용당하는 **대상**이 폐지/개명/이관됐냐 | "「국가균형발전 특별법」 제17조제2항" → 폐지·이관됨 |
|
|
20
|
+
|
|
21
|
+
방향이 반대다. 전자는 텍스트 안의 가짜를 잡고, 후자는 텍스트 안의 죽은 참조를 잡는다.
|
|
22
|
+
|
|
23
|
+
> **핵심 함정:** 법명을 단순 문자열 치환으로 정규화하면, 분할 이관·조문번호 변경 케이스에서 **틀린 곳을 더 틀린 곳으로** 보내는 오정정이 발생한다. 그래서 lawdangle은 **탐지·제안**만 하고, 정정을 자동 적용하지 않는다.
|
|
24
|
+
|
|
25
|
+
## 5분류
|
|
26
|
+
|
|
27
|
+
| 코드 | 유형 | 작동 | 정비 방법 | 심각성 |
|
|
28
|
+
|---|---|---|---|---|
|
|
29
|
+
| **A** | 단순 개명 | O | 법명만 교체 | 5 (낮음) |
|
|
30
|
+
| **B** | 전부개정·이관 (조문 대응) | O | 법명+조문 교체 | 4 |
|
|
31
|
+
| **C** | 분할 승계 (1:N) | O | 호 단위 수동 매핑 | 2 (높음) |
|
|
32
|
+
| **D** | 사문화 (전제 소멸) | 보통 자체조문으로 작동 | 인용부 삭제·재구성 | 3 |
|
|
33
|
+
| **E** | 순수 폐지 (빈 참조) | X 가능 | 입법 보완 | 1 (최상) |
|
|
34
|
+
|
|
35
|
+
심각성 순위(정비 우선순위): **E > C > D > B > A**.
|
|
36
|
+
**C / D / E는 자동 단정하지 않고** 근거를 붙여 수동 확인 플래그를 단다 — 이 절제가 이 도구의 신뢰도 핵심이다.
|
|
37
|
+
|
|
38
|
+
### 자동 판정 범위 (라이브 API 기준)
|
|
39
|
+
|
|
40
|
+
| 판정 | 방식 | 신뢰도 |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| 현행 / 폐지 | `target=law` · `eflaw` 조회 | 자동 (높음) |
|
|
43
|
+
| **A — 개명** | **법령ID 연속성**(옛이름 없음 + 같은 ID가 현행에 다른 이름) + **인용 조문 생존 검증** | 자동 (높음) |
|
|
44
|
+
| **A — 공식 약칭** | 공식 약칭사전(`lsAbrv`, 약 2,600건) → 풀네임 해소 | 자동 (높음) |
|
|
45
|
+
| **A — 부분명(앞부분 생략)** | 보수적 `endswith` 부분일치 → 풀네임 | 자동 (중간) |
|
|
46
|
+
| B — 개명+조문이동 | 개명이지만 인용 조문이 현행본에서 삭제/재번호 → `--map` 권고 | 자동 후보 (중간) |
|
|
47
|
+
| B — 이관 / C — 분할 | 폐지 + 후속법 후보(부칙 역추적 + 제개정이유) → 조문대응 수동 | 수동 플래그 + 후보 |
|
|
48
|
+
| D — 사문화 / E — 폐지 | 폐지 + 흡수신호(일반회계) / 후속법 없음 | 수동 플래그 |
|
|
49
|
+
| 비공식 약칭(화관법 등) | 정확매칭 실패 → UNKNOWN(오매칭 회피) | — |
|
|
50
|
+
|
|
51
|
+
## 설치
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install lawdangle-kr
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Python 3.10+ 필요. 라이브 조회에는 무료 **법제처 OPEN API** 인증키(OC)가 필요하다 — <https://open.law.go.kr> 에서 발급.
|
|
58
|
+
|
|
59
|
+
## 빠른 시작
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 권장 — 법령명으로 그 법의 현행 본문을 직접 가져와 분석.
|
|
63
|
+
# 붙여넣기 불필요 + 판례 혼입 없음 + 인용하는 조(citing_article)까지 채워짐.
|
|
64
|
+
LAW_OC=your_oc lawdangle --law "공유수면 관리 및 매립에 관한 법률" --format csv
|
|
65
|
+
|
|
66
|
+
# 텍스트 파일 분석
|
|
67
|
+
LAW_OC=your_oc lawdangle path/to/law.txt --format summary
|
|
68
|
+
|
|
69
|
+
# 오프라인 데모 (연혁 fixture — API 키 불필요)
|
|
70
|
+
lawdangle examples/sample_corpus.txt \
|
|
71
|
+
--fixture test/fixtures/gongyusumyeon.json test/fixtures/deunggi.json \
|
|
72
|
+
--format csv
|
|
73
|
+
|
|
74
|
+
# --deep: B·개명+조문이동 건에 구체 대응 조문까지 매핑
|
|
75
|
+
LAW_OC=your_oc lawdangle path/to/law.txt --format csv --deep
|
|
76
|
+
|
|
77
|
+
# 조문 대응 도우미 (옛 조문 → 후속법 조문)
|
|
78
|
+
# 후속법 생략 시 자동 발견(분할 이관도 OK):
|
|
79
|
+
LAW_OC=your_oc lawdangle --map "국가균형발전 특별법" "제17조제2항"
|
|
80
|
+
# ...또는 후속법을 직접 지정:
|
|
81
|
+
LAW_OC=your_oc lawdangle --map "국가균형발전 특별법" "제17조제2항" \
|
|
82
|
+
"지역 산업위기 대응 및 지역경제 회복을 위한 특별법"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from lawdangle import run_law
|
|
87
|
+
from lawdangle.resolver import LawGoKrResolver
|
|
88
|
+
|
|
89
|
+
resolver = LawGoKrResolver("your_oc")
|
|
90
|
+
for r in run_law("공유수면 관리 및 매립에 관한 법률", resolver):
|
|
91
|
+
if r.category: # 현행/정상 인용은 건너뜀
|
|
92
|
+
print(r.citation.citing_article, r.citation.cited_law_name,
|
|
93
|
+
r.citation.cited_article, "→", r.category.name, "|", r.note)
|
|
94
|
+
|
|
95
|
+
# 인용 단위로 직접 다룰 수도 있다:
|
|
96
|
+
from lawdangle import parse_citations, classify
|
|
97
|
+
for c in parse_citations(open("law.txt", encoding="utf-8").read()):
|
|
98
|
+
res = classify(c, resolver.resolve(c.cited_law_name))
|
|
99
|
+
print(res.category, res.note)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 파이프라인
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
[법령 텍스트]
|
|
106
|
+
① Parser 「법령」 + 조문 인용 추출
|
|
107
|
+
② Resolver 법제처 API — 현행/개명/후속법 (캐시)
|
|
108
|
+
③ Classifier 5분류 의사결정 트리 (+ 신뢰도, 수동 플래그)
|
|
109
|
+
④ Reporter CSV / JSON / 요약
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 후속법 자동 추출 (라이브)
|
|
113
|
+
|
|
114
|
+
검색 API에는 후속·대체법 필드가 없지만, **폐지법률 상세(`lawService.do`)** 에 단서가 있다. resolver는 두 경로로 후속법을 역추적한다.
|
|
115
|
+
|
|
116
|
+
- **타법폐지 역추적** — 다른 법의 부칙으로 폐지(`타법폐지`)된 경우, 폐지본의 부칙 헤더 `부칙(폐지시킨 법) … 「self」은 폐지한다` 에서 **폐지시킨 법 = 후속법**을 직접 추출.
|
|
117
|
+
*예)* 저탄소 녹색성장 기본법 → 「기후위기 대응을 위한 탄소중립ㆍ녹색성장 기본법」; 국토이용관리법 → 「국토의 계획 및 이용에 관한 법률」.
|
|
118
|
+
- **제개정이유 산문** — 이유서의 「후속법」 추출 (부칙 '다른 법률의 개정'은 부수개정이라 제외, 법명형 꼬리말만).
|
|
119
|
+
- **흡수 신호**(`absorbed`) — "…일반회계로 통합" 같은 회계 흡수 표현 → 후속'법' 없음 = **D 강신호**.
|
|
120
|
+
|
|
121
|
+
후보 개수로 분류: **1개 → B(이관)**, **2개+ → C(분할)**, 0개+흡수 → D. 모두 수동 플래그.
|
|
122
|
+
|
|
123
|
+
### 본문 기반 후속법 발견 (분할 이관)
|
|
124
|
+
|
|
125
|
+
역추적한 후보는 *법 전체의 일반 승계자*라 **특정 조항의 승계자와 다를 수 있다**(분할 이관 함정). 예) 국가균형발전 특별법의 일반 승계자는 「지방자치분권 및 지역균형발전에 관한 특별법」이지만, §17②의 산업위기대응특별지역 제도는 **전혀 다른 법** 「지역 산업위기 대응 및 지역경제 회복을 위한 특별법」으로 갔다.
|
|
126
|
+
|
|
127
|
+
lawdangle은 이걸 자동으로 찾는다: 옛 조문 본문에서 **특징 개념어**(예: *산업위기대응특별지역*)를 뽑아 현행 **법령명**을 검색 → 그 제도를 *정의하는* 법이 떠오르고(대개 1건), 조문 매핑이 이어진다. 그래서 `--map`은 후속법을 **안 줘도** 동작하고, `--deep`은 발견한 후속법+조문을 B/C 결과에 붙인다.
|
|
128
|
+
|
|
129
|
+
## 조문 대응 반자동 매핑 (`--map`, `--deep`)
|
|
130
|
+
|
|
131
|
+
이관(B)에서 "옛 §17② → 후속법 어느 조문?"을 좁혀준다. 옛 법 연혁을 거슬러 **삭제 전 실본문**을 찾고(조문이 폐지 전 이미 삭제된 경우 대비), 후속법 현행 조문과 **본문 유사도**로 순위를 매긴다. 인용에 항(②)이 있으면 최상위 후속 조 안에서 **항 단위까지** 좁힌다(호 단위는 설계상 수동 — C).
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
$ lawdangle --map "국가균형발전 특별법" "제17조제2항" "지역 산업위기 대응 및 지역경제 회복을 위한 특별법"
|
|
135
|
+
|
|
136
|
+
옛 법령 : 「국가균형발전 특별법」 제17조 (실본문 20220113 시행본)
|
|
137
|
+
후속법령: 「지역 산업위기 대응 및 지역경제 회복을 위한 특별법」
|
|
138
|
+
판정 : 수동확인 — 복수 후보 경합(분할 승계 가능) (항 정밀화: 제1항 유사도 0.205)
|
|
139
|
+
후보(유사도순):
|
|
140
|
+
1. 제10조제1항 (0.18) ① 산업통상부장관은 … 지정 신청을 받은 경우에는 …
|
|
141
|
+
2. 제12조 (0.176) …
|
|
142
|
+
3. 제9조 (0.172) …
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
DESIGN의 정답 **§17②→§10**을 #1 후보(제10조제1항)로 제시하면서, §9·§10·§12가 근소 경합이라 **자동 확정하지 않고**(분할 승계 가능) 사람의 최종 대조로 넘긴다. 이것이 "틀린 곳을 더 틀린 곳으로 보내는 오정정"을 피하는 방식이다.
|
|
146
|
+
|
|
147
|
+
## 검증된 실증 2케이스 (회귀 고정값)
|
|
148
|
+
|
|
149
|
+
| 입력 | 인용 | 정답 |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| 공유수면법 §13①14 | 「국가균형발전 특별법」 §17② | **B** — 제도가 「지역 산업위기 대응 및 지역경제 회복을 위한 특별법」으로 이관 |
|
|
152
|
+
| 등기특별회계법 §3 | 「국유재산관리특별회계법」 §6 | **D** — 특별회계 폐지·일반회계 흡수, 인용 조문은 자체 각 호로 작동 |
|
|
153
|
+
|
|
154
|
+
이 2건은 `test/fixtures/`에 고정되어 있고, 출력이 B / D가 아니면 회귀 실패로 처리한다.
|
|
155
|
+
|
|
156
|
+
## 스코프 경계 (의도적으로 안 하는 것)
|
|
157
|
+
|
|
158
|
+
- 이 도구는 **탐지·분류**까지다. 자동 정정(법명 치환)은 기본 제공하지 않는다 — 정정은 *제안*으로만 내고, 적용은 사람이. (자동 치환은 B/C에서 오정정을 낳는다.)
|
|
159
|
+
- "폐지=흠결"로 단정하지 않는다. D도 B도 작동한다. 효력 마비는 E(+일부 D)뿐 — 리포트는 이 톤을 유지해 반박당하지 않는다.
|
|
160
|
+
- 공식 사전에 없는 비공식 약칭은 잘못 매칭하느니 UNKNOWN으로 둔다.
|
|
161
|
+
|
|
162
|
+
## 개발
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pip install -e ".[dev]"
|
|
166
|
+
pytest # 오프라인 테스트는 어디서나; 라이브 API 테스트는 OC 키 있을 때만
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
라이브 테스트는 `LAW_OC` 환경변수 또는 로컬 `.env`(`oc=...`)에서 키를 읽고, 없으면 건너뛴다.
|
|
170
|
+
|
|
171
|
+
자세한 설계는 [DESIGN.md](DESIGN.md) 참조.
|
|
172
|
+
|
|
173
|
+
## 라이선스
|
|
174
|
+
|
|
175
|
+
MIT — [LICENSE](LICENSE) 참조.
|