fix(knowledge): 검토내역 8건 반영 — 상한 제거·행정규칙 부칙 복원·회귀검사 신설

타 AI 검토내역(tmp/기술문서DB_검토내역_260814.md) 전수 검증 후 동의분 적용.

파이프라인:
- fix_fences.py units[:20] / collect_law.py 별표 limit=80·변경이력 [:40] 상한 제거
- gen_md.py 기준목록 출력을 운영본(04_참조_법령기준_목록.md) 경로로 정정
- README 구경로(docs/raw/law) 3곳 현행화, ruff format 일괄 적용
- verify_structure.py 신설: XML↔md 부칙·목·별표·CHANGELOG 행수 회귀검사

신규 발견 결함 2건 정정 (검토내역에 없던 것):
- 행정규칙 XML은 부칙내용이 부칙 직속(부칙단위 래퍼 없음) — 변환기가 미처리해
  행정규칙 md 22건에 부칙 전무. patch_law_md 확장 후 부칙 138건 복원
- patch_law_md 부칙 dedup 키가 괄호형 타법개정 헤더 미매칭 — 재실행 시
  기존 부칙 1,724건 중복 추가되는 비멱등 결함. 정규식 보강

재수집 (law.go.kr):
- 대기환경보전법 시행규칙 별표 PDF 78건 추가 확보 (80→158, 전수)
- CHANGELOG 절단 3건 재생성 (고용보험법 시행령 42·국가계약법 시행령 41·
  대기환경 45회 — API 연혁과 전건 일치 확인)

기술문서·데이터:
- _최종검증로그: 55문서 스냅샷 범위 명시 (현재 65문서, 신설 10건 미검증 주지)
- 제비율.md frontmatter에 조달청 제비율(2026.04.13)·국민연금법 출처 보강
- 원가_입력변수_사전: 장비대금 지급보증을 공사종류별 표로 정정, csv→json 표기
- labor_const JSON: 원문 노임 118건 전건 일치 확인 후 해시만 재동기화
  (원문 변경분 = 신뢰도 플래그 컬럼뿐, 단가 무변)

검증: verify_structure 결함 0건, patch_law_md 재실행 +0 (멱등),
원가 JSON 해시 56건 중 잔여 불일치 1건(coef_2026 — 사용자 협의 대상)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 07:47:36 +09:00
co-authored by Claude Fable 5
parent 46bcaf4f4a
commit 6df07f60a1
113 changed files with 1331 additions and 176 deletions
@@ -14,6 +14,7 @@
python patch_law_md.py --dry # 변경량만 출력
python patch_law_md.py # 저장
"""
import re
import sys
import xml.etree.ElementTree as ET
@@ -22,7 +23,9 @@ from collect_law import ROOT, T, clean
분류목록 = ("법률", "행정규칙")
STEM = re.compile(r"^(현행|교본시점)_\d{8}$")
부칙헤더 = re.compile(r"부칙\s*<[^>]*>")
# 괄호형 타법개정 헤더('부칙(통계법 시행령) <제20331호,…>')도 매칭 — 괄호형을
# 놓치면 키가 비어 매 실행마다 같은 부칙을 재추가한다 (2026-08-15 멱등성 결함 정정).
부칙헤더 = re.compile(r"부칙\s*(?:\([^)\n]*\))?\s*<[^>]*>")
def 부칙키(text):
@@ -64,7 +67,7 @@ def patch(md_path, xml_path):
# 앵커별로 묶어 한 번에 삽입
묶음 = {}
for 앵커, mv in 작업:
if not mv or mv in text: # 이미 있으면 건너뜀
if not mv or mv in text: # 이미 있으면 건너뜀
continue
if not 앵커:
앵커실패 += 1
@@ -78,22 +81,28 @@ def patch(md_path, xml_path):
if idx is None:
앵커실패 += len(목들)
continue
lines[idx + 1:idx + 1] = [f" {mv}" for mv in 목들]
lines[idx + 1 : idx + 1] = [f" {mv}" for mv in 목들]
목추가 += len(목들)
# ── 2) 부칙 보충 ──
부칙 = root.find("부칙")
if 부칙 is not None:
units = 부칙.findall("부칙단위")
# 법령(law)=부칙단위 래퍼 / 행정규칙(admrul)=부칙내용 직속 (2026-08-15 결함 정정
# — 직속형 미처리로 행정규칙 md 22건에 부칙이 통째로 빠져 있었다)
units = 부칙.findall("부칙단위") or 부칙.findall("부칙내용")
본문 = "\n".join(lines)
기존키 = {re.sub(r"\s+", "", m) for m in 부칙헤더.findall(본문)}
누락 = [b for b in units if (부칙키(T(b, "부칙내용")) or "") not in 기존키]
def 본문텍스트(b):
return T(b, "부칙내용") if b.tag == "부칙단위" else "".join(b.itertext())
누락 = [b for b in units if (부칙키(본문텍스트(b)) or "") not in 기존키]
if 누락:
if "## 부칙" not in 본문:
lines += ["", "## 부칙", ""]
add = []
for b in 누락:
body = clean(T(b, "부칙내용"))
body = clean(본문텍스트(b))
if not body.strip():
continue
for ln in body.split("\n"):
@@ -130,14 +139,18 @@ def main(dry=False):
md_path.write_text(orig, encoding="utf-8")
if b or m or f:
파일 += 1
print(f" {분류}/{folder.name}/{md_path.name}: 부칙+{b} 목+{m}"
+ (f" (앵커실패 {f})" if f else ""))
print(
f" {분류}/{folder.name}/{md_path.name}: 부칙+{b} 목+{m}"
+ (f" (앵커실패 {f})" if f else "")
)
합계[0] += b
합계[1] += m
합계[2] += f
print(f"\n파일 {파일}건 / 부칙 +{합계[0]} · 목 +{합계[1]}"
+ (f" · 앵커실패 {합계[2]}" if 합계[2] else "")
+ (" (dry-run — 원복함)" if dry else " 저장 완료"))
print(
f"\n파일 {파일}건 / 부칙 +{합계[0]} · 목 +{합계[1]}"
+ (f" · 앵커실패 {합계[2]}" if 합계[2] else "")
+ (" (dry-run — 원복함)" if dry else " 저장 완료")
)
if __name__ == "__main__":