Files
eomsangdonandClaude Fable 5 6df07f60a1 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>
2026-08-15 07:47:36 +09:00

95 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""기존 법령 md의 코드펜스(``` 부칙·개정문)를 인용블록으로 전환.
- 부칙: 같은 폴더 XML에서 절단 없이 다시 읽어 인용블록으로 교체(2500자 절단 복구)
- 개정문·제개정이유(CHANGELOG): 코드펜스 → 인용블록
본문·이미지 로컬화 결과는 건드리지 않는다(부칙/개정문 섹션만 치환).
"""
import re, sys
import xml.etree.ElementTree as ET
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
FENCE = "```"
def clean(t):
t = (t or "").replace(" ", " ")
return re.sub(r"[ \t]+", " ", t).strip()
def to_quote(text):
return "\n".join(f"> {ln}" if ln.strip() else ">" for ln in text.split("\n"))
def buchik_from_xml(xml_path):
"""XML에서 부칙 인용블록 md(절단 없음) 생성. '## 부칙' 섹션 문자열 반환."""
root = ET.parse(xml_path).getroot()
bc = root.find("부칙")
if bc is None:
return None
units = bc.findall("부칙단위")
if not units:
return None
out = ["## 부칙", ""]
# 상한 없음 — 부칙 전건 출력 (상한을 두면 부칙 절단 결함 재발, 2026-08-15)
for b in units:
body = clean(b.findtext("부칙내용") or "")
out.append(to_quote(body))
out.append("")
return "\n".join(out).rstrip() + "\n"
def fix_law_md(md_path, xml_path):
"""현행/교본시점 md: '## 부칙' ~ 다음 '## ' 사이를 XML 부칙으로 교체."""
text = md_path.read_text(encoding="utf-8")
if "## 부칙" not in text:
return 0
new_buchik = buchik_from_xml(xml_path)
if not new_buchik:
return 0
# '## 부칙' 부터 다음 '## ' 헤딩 전까지 잘라 교체
m = re.search(r"^## 부칙\s*$", text, re.M)
if not m:
return 0
start = m.start()
nxt = re.search(r"^## (?!부칙)", text[m.end() :], re.M)
end = m.end() + nxt.start() if nxt else len(text)
new = text[:start] + new_buchik + ("\n" + text[end:].lstrip("\n") if text[end:].strip() else "")
if new != text:
md_path.write_text(new, encoding="utf-8")
return 1
return 0
def fix_changelog(md_path):
"""CHANGELOG: 개정문/제개정이유 코드펜스 → 인용블록."""
text = md_path.read_text(encoding="utf-8")
if FENCE not in text:
return 0
def repl(m):
inner = m.group(1).strip()
return to_quote(inner)
new = re.sub(FENCE + r"\w*\n(.*?)\n?" + FENCE, repl, text, flags=re.S)
if new != text:
md_path.write_text(new, encoding="utf-8")
return 1
return 0
if __name__ == "__main__":
n_law = n_cl = 0
for md in ROOT.rglob("*.md"):
if "임도기술교본" in str(md) or "_pipeline" in str(md):
continue
if md.name.startswith(("현행_", "교본시점_")):
xml = md.with_suffix(".xml")
if xml.exists():
n_law += fix_law_md(md, xml)
elif md.name == "CHANGELOG.md":
n_cl += fix_changelog(md)
print(f"부칙 인용블록 전환 {n_law}개 / CHANGELOG {n_cl}개")