From d379acc59b934fd3b4d22ace3af7178a1158f498 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 18 Sep 2026 06:19:53 +0900 Subject: [PATCH] =?UTF-8?q?chore(tester):=20=E3=89=AE=2036=20=EC=9D=84=20?= =?UTF-8?q?=E3=80=8C=ED=95=A0=20=EC=9D=BC=20=EC=95=84=EB=8B=98=E3=80=8D=20?= =?UTF-8?q?=EC=B9=B8=EC=9C=BC=EB=A1=9C=20=EB=B9=BC=EA=B3=A0=20=EA=B0=88?= =?UTF-8?q?=EB=9E=98=20=EB=82=98=EB=88=94=20=C2=B7=20=E3=89=AF=20=EC=97=90?= =?UTF-8?q?=20=EC=A3=BC=EC=9D=B8=20=EB=8B=A4=20=EC=A0=81=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **① ㉮ 를 갈래에서 아주 뺌** — 장부에 섞여 있어 바퀴마다 「어긋남」으로 다시 세어지던 것. `_할일_아님` 칸으로 옮김(공종축 34 + 기초값 2) · **지우지는 않음**(나중에 사유를 보아야 함) 갈래 — 거절 자리 11 · 그 밖 18 · 원문 부재 4 · 부록 예시 1 ⚠ **세는 곳에서만 빼고 비교에는 넣음** — `load_known()` 이 그 칸도 읽게 함. 안 그러면 옮긴 줄이 다시 「새로 어긋남」으로 빨개짐 시험으로 못박음 — 칸이 있나 · 34줄인가 · 줄마다 **왜 할 일이 아닌지** 사유가 있나 · 갈래가 넷 중 하나인가 **② ㉯ 남의 몫에 주인 다 적음** — 갈래 없던 기계 줄 65 에도 적어 미정 0 랩탑 메인 87(md 읽기 22 + 기계 원천 벌 재추출 65) · 랩탑 서브 1(F0333 PDF 대조) **③ 남은 건수 — 실제 할 일만** ① 산림 공종 축 **0** (미확보 79 = 원문 부재 69 + 미판독 10, 열은 다 거절 자리) · 미귀속 표 22 는 ㉰ 에 ② 건설 공종 축 **1,053** (형태 미판정 891 + 밑수 미판독 162) — 뿌리는 「판정이 산림 표로만 맞춰진 것」이라 랩탑 메인 몫 ③ 확인 대기 **100** = ㉯ 92(다 남의 몫) + ㉰ 8 · 할 일 아님으로 뺀 것 36 ④ 사용자 판단 **8** — F0046·F0068 「1인」 밑수 · F0076 반쪽 「1km」 · 부록 14-2 표 셋 · F0405 증가율표 성격 · 이형철근 범위 6~7 ⇒ **내가 지금 할 수 있는 것은 0** — 다 남의 차례이거나 사용자 판단 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01QsGw1Dz9HmhuAxGisxmDPF --- .../fixtures/base_data_source_known.json | 51 +- .../fixtures/base_prices_source_known.json | 325 ++++++-- .../work_item_master_source_known.json | 752 ++++++++++-------- .../tester/test_base_data_source_match.py | 11 +- .../tester/test_base_prices_source_match.py | 11 +- .../test_work_item_master_source_match.py | 38 +- 6 files changed, 754 insertions(+), 434 deletions(-) diff --git a/resources/tester/fixtures/base_data_source_known.json b/resources/tester/fixtures/base_data_source_known.json index e43d88c9..01ccb157 100644 --- a/resources/tester/fixtures/base_data_source_known.json +++ b/resources/tester/fixtures/base_data_source_known.json @@ -29,24 +29,7 @@ "masonry_back_length": {}, "stone_kind": {}, "masonry_class": {}, - "machine_productivity": { - "dump_material/발파암/truck_efficiency": { - "md": [], - "data": "0.9", - "why": "10-12-3 운반 식에 E 가 없음 — 토사·암절취의 0.9 를 이어 씀", - "class": "㉮ 영영 할 일 아님", - "class_why": "원문 식에 E 가 없어 토사·암절취 0.9 를 이어 씀 — 사유 적음" - }, - "dump_material/토사/loading_efficiency": { - "md": [ - 0.75 - ], - "data": "0.60", - "why": "본문 E0=0.75 대신 10-12-1 [주]⑤ 표 「임도 0.60(불량)」 을 씀 — 자료 loading_note 에 사유", - "class": "㉮ 영영 할 일 아님", - "class_why": "원문 [주]⑤ 표 비고 「임 도」로 0.60 확정 — 사유 적음" - } - }, + "machine_productivity": {}, "_갈래": { "센 날": "2026-09-18", "셈": { @@ -54,5 +37,37 @@ "㉯ 남이 고쳐야 풀림": 0, "㉰ 사용자 판단 필요": 4 } + }, + "_할일_아님": { + "잣대": "㉮ — **영영 할 일이 아닌 것.** 원문에 없음 · 거절 자리 · 판정으로 닫힘. 바퀴마다 다시 세어지지 않게 갈래에서 **아주 빼** 여기 둔다. ⚠ 지우지는 않는다 — 되짚을 수 있어야 한다.", + "옮긴 날": "2026-09-18", + "셈": { + "machine_productivity": 2 + }, + "줄": { + "machine_productivity": { + "dump_material/발파암/truck_efficiency": { + "md": [], + "data": "0.9", + "why": "10-12-3 운반 식에 E 가 없음 — 토사·암절취의 0.9 를 이어 씀", + "class": "㉮ 영영 할 일 아님", + "class_why": "원문 식에 E 가 없어 토사·암절취 0.9 를 이어 씀 — 사유 적음", + "갈래": "거절 자리" + }, + "dump_material/토사/loading_efficiency": { + "md": [ + 0.75 + ], + "data": "0.60", + "why": "본문 E0=0.75 대신 10-12-1 [주]⑤ 표 「임도 0.60(불량)」 을 씀 — 자료 loading_note 에 사유", + "class": "㉮ 영영 할 일 아님", + "class_why": "원문 [주]⑤ 표 비고 「임 도」로 0.60 확정 — 사유 적음", + "갈래": "거절 자리" + } + } + }, + "갈래별": { + "거절 자리": 2 + } } } diff --git a/resources/tester/fixtures/base_prices_source_known.json b/resources/tester/fixtures/base_prices_source_known.json index 85bb85dd..d21660db 100644 --- a/resources/tester/fixtures/base_prices_source_known.json +++ b/resources/tester/fixtures/base_prices_source_known.json @@ -14,12 +14,18 @@ "가격 8201-0100 machine_name": { "source": "3D GNSS 머신 가이던스(굴착기용)", "data": "", - "why": "자료 이름이 빔 — 원문 손료표 머리 `(코드) 이름` 엔 이름 있음" + "why": "자료 이름이 빔 — 원문 손료표 머리 `(코드) 이름` 엔 이름 있음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "손료 8201-0100 machine_name": { "source": "3D GNSS 머신 가이던스(굴착기용)", "data": "배관파이프시 간 당(10-7)분류번호규 격7995-0050ø50-2.6m5,0008-3-9 [80]스마트 건설장비(8201) 3D GNSS 머신 가이던스(굴착기용)", - "why": "자료 이름 칸에 원문 뭉개진 손료 글이 통째로 들어감 — 자료 이름 틀림" + "why": "자료 이름 칸에 원문 뭉개진 손료 글이 통째로 들어감 — 자료 이름 틀림", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "셈 연료 코드 수": { "source": 201, @@ -34,7 +40,10 @@ "labor_blind": { "건설 pdf": { "what": "3. 개별직종 노임단가.md", - "why": "원문 md 는 PDF 를 옮긴 것 — PDF 자체와의 옮김 틀림은 못 봄" + "why": "원문 md 는 PDF 를 옮긴 것 — PDF 자체와의 옮김 틀림은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "건설 별칭 이름": { "what": [ @@ -54,19 +63,31 @@ "labor_op_general", "labor_op_truck" ], - "why": "별칭 이름은 프로그램이 지은 것 — 가리키는 코드가 원문에 있는지만 봄" + "why": "별칭 이름은 프로그램이 지은 것 — 가리키는 코드가 원문에 있는지만 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 labor_mfg.unit": { "what": "원문과 안 맞댐", - "why": "`KRW/day` 는 프로그램 표기 — 원문 글(원·1일)과 글자로 안 맞댐" + "why": "`KRW/day` 는 프로그램 표기 — 원문 글(원·1일)과 글자로 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 labor_rate.unit": { "what": "원문과 안 맞댐", - "why": "`KRW/day` 는 프로그램 표기 — 원문 글(원·1일)과 글자로 안 맞댐" + "why": "`KRW/day` 는 프로그램 표기 — 원문 글(원·1일)과 글자로 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "제조 pdf": { "what": "2026상반기_중소제조업_직종별_임금조사_중소기업중앙회.md", - "why": "원문 md 는 PDF 를 옮긴 것 — PDF 자체와의 옮김 틀림은 못 봄" + "why": "원문 md 는 PDF 를 옮긴 것 — PDF 자체와의 옮김 틀림은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" } }, "machine_blind": { @@ -77,7 +98,10 @@ "내용 시간", "연간 표준 가동 시간" ], - "why": "복원 뒤에도 원문 표에 계수 칸이 없음 — 머리 「시 간 당(10-7)」 은 표 위 글줄로만 · 값은 원문에 없음" + "why": "복원 뒤에도 원문 표에 계수 칸이 없음 — 머리 「시 간 당(10-7)」 은 표 위 글줄로만 · 값은 원문에 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "손료 계수 칸 없는 표 7995": { "what": [ @@ -85,118 +109,202 @@ "규 격", "내용 시간" ], - "why": "복원 뒤에도 원문 표에 계수 칸이 없음 — 머리 「시 간 당(10-7)」 은 표 위 글줄로만 · 값은 원문에 없음" + "why": "복원 뒤에도 원문 표에 계수 칸이 없음 — 머리 「시 간 당(10-7)」 은 표 위 글줄로만 · 값은 원문에 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "손료보정 수 자리 가름": { "what": "뭉개진 글의 이어 붙은 수(2510)를 통째로 맞댐", - "why": "8-1-7 가산비율 표가 글로 뭉개져 `2510` 처럼 붙음 — 25·10 인지 2·510 인지 못 가름" + "why": "8-1-7 가산비율 표가 글로 뭉개져 `2510` 처럼 붙음 — 25·10 인지 2·510 인지 못 가름", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_fuel_rate.parse_method": { "what": "원문과 안 맞댐", - "why": "빌더가 남긴 읽기 방법 표시 — 원문 칸 아님" + "why": "빌더가 남긴 읽기 방법 표시 — 원문 칸 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_fuel_rate.source_tables.section": { "what": "원문과 안 맞댐", - "why": "8-4 원표의 절 제목 — 표 차례로 맞대므로 글자는 안 봄(원표 머리·줄은 맞댐)" + "why": "8-4 원표의 절 제목 — 표 차례로 맞대므로 글자는 안 봄(원표 머리·줄은 맞댐)", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_fuel_rate.specification": { "what": "원문과 안 맞댐", - "why": "규격 칸이 한 칸에 여러 값·여러 열로 뭉쳐 가르기 어려움 — 안 맞댐" + "why": "규격 칸이 한 칸에 여러 값·여러 열로 뭉쳐 가르기 어려움 — 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_loss_coef.loss_coefficient_per_hour": { "what": "원문과 안 맞댐", - "why": "계산 칸(계 × 10⁻⁷) — 원문 `계`(source_coefficient_1e_minus_7)만 맞댐" + "why": "계산 칸(계 × 10⁻⁷) — 원문 `계`(source_coefficient_1e_minus_7)만 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_loss_coef.note": { "what": "원문과 안 맞댐", - "why": "데스크탑 서브가 붙인 설명 글 — 값 아님" + "why": "데스크탑 서브가 붙인 설명 글 — 값 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_loss_coef.specification": { "what": "원문과 안 맞댐", - "why": "규격 칸이 한 칸에 여러 값·여러 열로 뭉쳐 가르기 어려움 — 안 맞댐" + "why": "규격 칸이 한 칸에 여러 값·여러 열로 뭉쳐 가르기 어려움 — 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_operator_map.rules.alias": { "what": "원문과 안 맞댐", - "why": "프로그램이 지은 이름·출처 표기" + "why": "프로그램이 지은 이름·출처 표기", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_operator_map.rules.rule_id": { "what": "원문과 안 맞댐", - "why": "프로그램이 지은 이름·출처 표기" + "why": "프로그램이 지은 이름·출처 표기", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_operator_map.rules.source_section": { "what": "원문과 안 맞댐", - "why": "프로그램이 지은 이름·출처 표기" + "why": "프로그램이 지은 이름·출처 표기", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_price.specification": { "what": "원문과 안 맞댐", - "why": "가격표엔 규격 칸이 없음(기종·코드·가격만)" + "why": "가격표엔 규격 칸이 없음(기종·코드·가격만)", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_rock_adj.rules.exception": { "what": "원문과 안 맞댐", - "why": "[주]① 예외 글을 프로그램 말로 옮긴 칸 — 글자로 안 맞댐" + "why": "[주]① 예외 글을 프로그램 말로 옮긴 칸 — 글자로 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mach_rock_adj.rules.exclusion": { "what": "원문과 안 맞댐", - "why": "[주]① 예외 글을 프로그램 말로 옮긴 칸 — 글자로 안 맞댐" + "why": "[주]① 예외 글을 프로그램 말로 옮긴 칸 — 글자로 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0025": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0050": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0100": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0125": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0150": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0200": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0250": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0350": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0450": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0500": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7505-0700": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7993-0020": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 못 읽은 줄 7994-0050": { "what": "원문 표 토막 수가 안 맞음", - "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄" + "why": "원문 운전경비 표 토막 수가 코드 수와 안 맞음(`동력 15.1㎾`·`73.7+ 휘발유54.5` 등) — 이 줄 자료 값은 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 표 3108-0040": { "codes": [ "3108-0040", "3901-0300" ], - "why": "원문 운전경비 표 토막 수가 안 맞음 — 이 표 줄은 못 읽음" + "why": "원문 운전경비 표 토막 수가 안 맞음 — 이 표 줄은 못 읽음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 표 5801-0045": { "codes": [ @@ -227,36 +335,57 @@ "7101-0450", "7811-0120" ], - "why": "원문 운전경비 표 토막 수가 안 맞음 — 이 표 줄은 못 읽음" + "why": "원문 운전경비 표 토막 수가 안 맞음 — 이 표 줄은 못 읽음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "연료 표 7812-0005": { "codes": [ "7812-0005", "7994-0050" ], - "why": "원문 운전경비 표 토막 수가 안 맞음 — 이 표 줄은 못 읽음" + "why": "원문 운전경비 표 토막 수가 안 맞음 — 이 표 줄은 못 읽음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "운전사 기종별 판단": { "what": 121, - "why": "기종→운전사 구분(explicit_mappings)은 사람 판단 — 원문 8-1-3 5호는 기종 이름 글만 있음" + "why": "기종→운전사 구분(explicit_mappings)은 사람 판단 — 원문 8-1-3 5호는 기종 이름 글만 있음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" } }, "material_blind": { "API 스냅숏": { "what": "나라장터_시설공통자재_2026-08-14.json", - "why": "원문이 API 로 받은 스냅숏 — 게시처(나라장터·오피넷) 값 자체와는 못 맞댐" + "why": "원문이 API 로 받은 스냅숏 — 게시처(나라장터·오피넷) 값 자체와는 못 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "같은 때 공시 20696249": { "what": 2, - "why": "같은 공시 일시에 값 실린 공시가 둘 — 원천이 어느 것이 정본인지 안 알려 줌 · 자료는 그중 하나와 같음" + "why": "같은 공시 일시에 값 실린 공시가 둘 — 원천이 어느 것이 정본인지 안 알려 줌 · 자료는 그중 하나와 같음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "같은 때 공시 20696250": { "what": 2, - "why": "같은 공시 일시에 값 실린 공시가 둘 — 원천이 어느 것이 정본인지 안 알려 줌 · 자료는 그중 하나와 같음" + "why": "같은 공시 일시에 값 실린 공시가 둘 — 원천이 어느 것이 정본인지 안 알려 줌 · 자료는 그중 하나와 같음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 mat_price.price_adopted_reason": { "what": "원문과 안 맞댐", - "why": "빌더가 적은 까닭 글 — 원문 칸 아님" + "why": "빌더가 적은 까닭 글 — 원문 칸 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "원천 없는 묶음": { "what": [ @@ -264,7 +393,10 @@ "ready_mixed_concrete", "asphalt_concrete" ], - "why": "철근·레미콘·아스콘은 종합쇼핑몰 원천이 저장소에 없음 — 값 못 봄" + "why": "철근·레미콘·아스콘은 종합쇼핑몰 원천이 저장소에 없음 — 값 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" } }, "oil_blind": { @@ -273,31 +405,52 @@ "유가_전국평균_2026-08-14.json", "유가_시도별_2026-09-09.json" ], - "why": "원문이 API 로 받은 스냅숏 — 게시처(나라장터·오피넷) 값 자체와는 못 맞댐" + "why": "원문이 API 로 받은 스냅숏 — 게시처(나라장터·오피넷) 값 자체와는 못 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "시도 거래일": { "what": "시도별 원천에 거래일 칸이 없음 — 수집일과만 맞댐", - "why": "시도별 원천 줄에 거래일이 없어 `수집일` 로만 맞댐" + "why": "시도별 원천 줄에 거래일이 없어 `수집일` 로만 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 oil.scope": { "what": "원문과 안 맞댐", - "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음" + "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 oil.unit": { "what": "원문과 안 맞댐", - "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음" + "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 oil_regional.scope": { "what": "원문과 안 맞댐", - "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음" + "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 oil_regional.source_product_name": { "what": "원문과 안 맞댐", - "why": "시도별 원천에 상품 이름 칸 없음" + "why": "시도별 원천에 상품 이름 칸 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 oil_regional.unit": { "what": "원문과 안 맞댐", - "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음" + "why": "프로그램 표기(KRW/L · national_average) — 원천에 칸 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" } }, "rate_blind": { @@ -311,59 +464,101 @@ "safety_management_cost", "scale_bracket" ], - "why": "끝수·산식 처리 규칙 — 코드 규칙이라 원문 대조 대상 아님(거울 시험 몫)" + "why": "끝수·산식 처리 규칙 — 코드 규칙이라 원문 대조 대상 아님(거울 시험 몫)", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_environment.forest_road_selection_status": { "what": "pending", - "why": "임도 공종 선택 미결 표시 — 사용자 협의 몫" + "why": "임도 공종 선택 미결 표시 — 사용자 협의 몫", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_environment.minimum_estimated_amount_krw": { "what": 100000000, - "why": "xlsx 환경보전비 칸에 1억 기준이 없음(1억은 퇴직공제부금 칸) — 근거 원문이 sources 에 없어 못 봄" + "why": "xlsx 환경보전비 칸에 1억 기준이 없음(1억은 퇴직공제부금 칸) — 근거 원문이 sources 에 없어 못 봄", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_equipment_payment_guarantee.base_note": { "what": "코드(`B09_Estimation_Statutory.STATUTORY_ITEMS`)가 직접공사비로 계산하고 있는 것과 맞춘 표기.", - "why": "설명 글" + "why": "설명 글", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_overhead.basis": { "what": "조달청 「2026년 토목·조경·산업환경설비공사 원가계산 간접공사비(제비율) 적용기준」(2026-04-13). ⚠ 구간 라벨의 `billion` 은 **십억 원** — `lt_5_billion` 은 50억 미만이다. 종합(토목·조경·산업설비)은 5억미만~30억~50억 8 · 50~100억·100~300억 6.5 · 300~1000억 5 · 1000억이상 4.5, 전문 등은 5억미만 8 · 5~30억 6.5 · 30~50억·50~100억 5 · 100억이상 4.5.", - "why": "데스크탑 서브가 붙인 설명 글 — 값 아님" + "why": "데스크탑 서브가 붙인 설명 글 — 값 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_overhead.specialty_note": { "what": "⚠ 2026-09-18 **300억 이상 두 구간이 통째로 빠져 있었음** — 마지막이 「100억-300억 미만」으로 닫혀 300억 넘는 전문공사가 어느 구간에도 안 걸렸음. 원본 XLSX(BL35:BR46) 대로 300~1000억 · 1000억 이상을 더함(둘 다 4.5%). 값은 원문 그대로.", - "why": "데스크탑 서브가 붙인 설명 글 — 값 아님" + "why": "데스크탑 서브가 붙인 설명 글 — 값 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_performance_guarantee_fee.typical_forest_road_applicability": { "what": "not_applicable", - "why": "임도 적용 판단 — 원문 칸 아님" + "why": "임도 적용 판단 — 원문 칸 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_safety_base.amount_bracket": { "what": "500_million_to_5_billion", - "why": "다른 칸을 가리키는 표시 — 값 아님" + "why": "다른 칸을 가리키는 표시 — 값 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_safety_base.embedded_in": { "what": "rate_safety_pct.brackets", - "why": "다른 칸을 가리키는 표시 — 값 아님" + "why": "다른 칸을 가리키는 표시 — 값 아님", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_safety_pct.base_rule": { "what": "min_of_two_formulas", - "why": "도급자관급 포함 시 두 식 중 작은 값 — xlsx 글(EB11·EB13) 해석이라 안 맞댐" + "why": "도급자관급 포함 시 두 식 중 작은 값 — xlsx 글(EB11·EB13) 해석이라 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_safety_pct.base_with_owner_supplied_material": { "what": "minimum_of_statutory_two_formulas", - "why": "도급자관급 포함 시 두 식 중 작은 값 — xlsx 글(EB11·EB13) 해석이라 안 맞댐" + "why": "도급자관급 포함 시 두 식 중 작은 값 — xlsx 글(EB11·EB13) 해석이라 안 맞댐", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_vat.base": { "what": "construction_cost_before_vat", - "why": "부가세 — 근거(부가가치세법)가 sources 에 없음" + "why": "부가세 — 근거(부가가치세법)가 sources 에 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "안 잰 칸 rate_vat.rate_percent": { "what": 10.0, - "why": "부가세 — 근거(부가가치세법)가 sources 에 없음" + "why": "부가세 — 근거(부가가치세법)가 sources 에 없음", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" }, "조달청 요약표": { "what": "2026.04.13_조달청_토목공사_제비율.xlsx", - "why": "xlsx 는 조달청 요약표 — 각 고시 원문과는 안 맞댐(연금만 법령과 맞댐)" + "why": "xlsx 는 조달청 요약표 — 각 고시 원문과는 안 맞댐(연금만 법령과 맞댐)", + "class": "㉯ 남이 고쳐야 풀림", + "owner": "랩탑 메인", + "owner_ask": "기계 원천 벌(mach_base_2026.json) 재추출 — 그 추출기가 이 저장소에 없어 우리가 못 돌림" } } } diff --git a/resources/tester/fixtures/work_item_master_source_known.json b/resources/tester/fixtures/work_item_master_source_known.json index 51ebe10f..8918d837 100644 --- a/resources/tester/fixtures/work_item_master_source_known.json +++ b/resources/tester/fixtures/work_item_master_source_known.json @@ -78,29 +78,6 @@ "class": "㉰ 사용자 판단 필요", "class_why": "⚠ 마스터 「1km」는 반쪽 — 원문은 「인/1일 1km당」으로 분모가 둘(일·km)" }, - "F0132": { - "section": "5-22-4", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(일당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "「일당」은 품의 단위(하루에 얼마)라 밑수로 안 받음" - }, - "F0145": { - "section": "5-28-2", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(일 당)", - "master": null, - "why": "작업조 표(시공량 열이 밑수) — 마스터가 일부러 비움(c3da333e 와 같은 까닭) · 원문 「(일 당)」 은 코덱스 복원(61f35108)", - "class": "㉮ 영영 할 일 아님", - "class_why": "작업조 표 — 밑수가 시공량 열이라 일부러 비움" - }, "F0173": { "section": "7-2", "md": null, @@ -140,17 +117,6 @@ "owner": "랩탑 메인", "owner_ask": "대조 시험의 **md 밑수 읽기**를 넓혀 줄 것 — 원문이 절 이름·본문·표머리에 적어 둔 자리를 md 쪽이 못 읽어 「마스터가 더 가짐」으로 뜸(마스터 값이 원문과 같음)" }, - "F0257": { - "section": "9-11-1", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(단위: 일당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "「일당」 · 계수표" - }, "F0322": { "section": "10-13-1", "md": null, @@ -176,83 +142,6 @@ "class_why": "위와 같음", "owner": "랩탑 메인", "owner_ask": "대조 시험의 **md 밑수 읽기**를 넓혀 줄 것 — 원문이 절 이름·본문·표머리에 적어 둔 자리를 md 쪽이 못 읽어 「마스터가 더 가짐」으로 뜸(마스터 값이 원문과 같음)" - }, - "F0338": { - "section": "12-6", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(단위: 일당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "작업조 표(포장공 3인·시공량)" - }, - "F0339": { - "section": "12-7-1", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(단위: 일당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "작업조 표 — 넣으면 호표가 사라짐(2026-09-17 유로폼에서 실증)" - }, - "F0340": { - "section": "12-7-2", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(단위: 일당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "위와 같음" - }, - "F0341": { - "section": "12-8", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(단위: 일당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "위와 같음" - }, - "F0395": { - "section": "12-38-3", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(단위: 일 당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "위와 같음" - }, - "F0447": { - "section": "13-15-2", - "md": [ - 1.0, - "조" - ], - "md_evidence": "(단위: 인/조)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "「인/조」 — 「조」는 세는 단위가 아님" - }, - "F0449": { - "section": "13-16-2", - "md": [ - 1.0, - "일" - ], - "md_evidence": "(단위: 일 당)", - "master": null, - "class": "㉮ 영영 할 일 아님", - "class_why": "위와 같음" } }, "notes": { @@ -477,108 +366,8 @@ "owner_ask": "`md_notes` 의 이어짐 규칙(`_NOTE_CONT`)에 **「- 숫자.」 로 시작하는 [주] 안쪽 규칙**을 더해 줄 것 — 지금은 「- ①②」·「- -」·「※*·」만 이어짐으로 봐서 「- 4. 고철공제 : A=…」에서 끊김. ⚠ 새 절 제목과 가르는 것이 일(알맹이 없는 짧은 줄만 제목)" } }, - "form": { - "F0002": { - "section": "1-2-2", - "master": "reference", - "md_mark": "직공인부", - "class": "㉮ 영영 할 일 아님", - "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐" - }, - "F0007": { - "section": "1-2-7", - "master": "reference", - "md_mark": "㎥", - "class": "㉮ 영영 할 일 아님", - "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐" - }, - "F0038": { - "section": "1-4-26", - "master": "reference", - "md_mark": "적용시공량", - "class": "㉮ 영영 할 일 아님", - "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐" - }, - "F0227": { - "section": "8-6-1", - "master": "reference", - "md_mark": "유효 살포량/1회", - "why": "사람 판정 reference(Forms.py) — 기종별 희석배수별 1회 살포량 계산표", - "class": "㉮ 영영 할 일 아님", - "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐" - }, - "F0257": { - "section": "9-11-1", - "master": "coefficient", - "md_mark": "ℓ", - "class": "㉮ 영영 할 일 아님", - "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐" - }, - "F0325": { - "section": "11-1", - "master": "reference", - "md_mark": "비계공", - "class": "㉮ 영영 할 일 아님", - "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐" - }, - "F0454": { - "section": "14-2", - "master": "reference", - "md_mark": "작업량", - "class": "㉮ 영영 할 일 아님", - "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐" - } - }, + "form": {}, "form_blind": { - "F0046": { - "section": "2-1-1", - "master": "requirement", - "why": "기계별 연료표(천공기 주연료 3ℓ·잡품 95%) — 품 낱말 없음. 같은 모양 F0044·F0045·F0047 은 사람 판정 reference 인데 이것만 requirement", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0062": { - "section": "2-1-12", - "master": "requirement", - "why": "값 칸이 「층적부피에 따라 별도계산」·「〃」 — 숫자 없음", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0140": { - "section": "5-26-1", - "master": "requirement", - "why": "머리 소실 — 토성×깊이 숫자만 남음(사람 판정: 100㎡당 보통인부)", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0141": { - "section": "5-26-2", - "master": "requirement", - "why": "머리 소실 — 위와 같음", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0142": { - "section": "5-26-3", - "master": "requirement", - "why": "머리 소실 — 위와 같음", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0195": { - "section": "7-14", - "master": "productivity", - "why": "칸이 뭉침(「㎥ 0.06∼0.07 4.8∼6.0」) — 원문 md 가 병합 칸을 못 풂", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0237": { - "section": "8-11", - "master": "productivity", - "why": "작업량이 식 글(「Q = 3.5 ㎥/hr」)", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, "F0333": { "section": "12-1-3", "master": "requirement", @@ -588,27 +377,6 @@ "owner": "랩탑 서브", "owner_ask": "PDF ↔ md 대조 — 이 표의 **몸 줄이 앞 표와 똑같음**. PDF 로 어느 쪽이 맞는지 짚어 줄 것" }, - "F0342": { - "section": "12-9-1", - "master": "requirement", - "why": "수량 칸이 비어 있음 — 원문에 값 없음(「반중력식 옹벽 참조」)", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0343": { - "section": "12-9-2", - "master": "requirement", - "why": "수량 칸이 비어 있음 — 원문에 값 없음", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, - "F0344": { - "section": "12-9-3", - "master": "requirement", - "why": "수량 칸이 비어 있음 — 원문에 값 없음", - "class": "㉮ 영영 할 일 아님", - "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠" - }, "F0405": { "section": "13-4-1", "master": "requirement", @@ -617,120 +385,7 @@ "class_why": "증가율(%) 표 — requirement 가 맞는지 사람 확인" } }, - "cost_rule": { - "F0369": { - "section": "12-22", - "master": "sub_requirement", - "md_rows": [ - [ - "재료비" - ], - [ - "설치비", - "재료비의 5%" - ] - ], - "class": "㉮ 영영 할 일 아님", - "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘" - }, - "F0379": { - "section": "12-29", - "master": "requirement", - "md_rows": [ - [ - "재료비", - "", - "필요수량" - ], - [ - "설치비", - "재료비의 5%" - ] - ], - "why": "⚠ 같은 꼴 6표는 reference 인데 이것만 requirement — 설치비 = 재료비의 5%", - "class": "㉮ 영영 할 일 아님", - "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘" - }, - "F0382": { - "section": "12-31", - "master": "sub_requirement", - "md_rows": [ - [ - "재료비" - ], - [ - "설치비", - "주재료비의 5%" - ] - ], - "class": "㉮ 영영 할 일 아님", - "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘" - }, - "F0383": { - "section": "12-32", - "master": "sub_requirement", - "md_rows": [ - [ - "재료비", - "P.V.C ∅54m/m" - ], - [ - "설치비", - "주재료비의 10%" - ] - ], - "class": "㉮ 영영 할 일 아님", - "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘" - }, - "F0386": { - "section": "12-34-2", - "master": "sub_requirement", - "md_rows": [ - [ - "재료비", - "1회 기준 46.1%" - ], - [ - "노무비", - "1회 기준 47.1%" - ] - ], - "class": "㉮ 영영 할 일 아님", - "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘" - }, - "F0388": { - "section": "12-34-4", - "master": "sub_requirement", - "md_rows": [ - [ - "재료비", - "JOINT FILLER" - ] - ], - "class": "㉮ 영영 할 일 아님", - "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘" - }, - "F0390": { - "section": "12-36", - "master": "sub_requirement", - "md_rows": [ - [ - "제작비", - "견적처리" - ], - [ - "운송비", - "견적처리" - ], - [ - "설치비", - "견적처리" - ] - ], - "class": "㉮ 영영 할 일 아님", - "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘" - } - }, + "cost_rule": {}, "_갈래": { "잣대": "㉮ 는 집계에서 뺀다 — 영영 할 일이 아닌 것(원문에 없음·거절 자리·판정으로 닫힘). ㉯ 는 남의 몫(코덱스 md 읽기·랩탑 메인 시험). ㉰ 만 사람이 정할 자리다.", "센 날": "2026-09-18", @@ -743,5 +398,408 @@ "랩탑 메인": 22, "랩탑 서브": 1 } + }, + "_할일_아님": { + "잣대": "㉮ — **영영 할 일이 아닌 것.** 원문에 없음 · 거절 자리 · 판정으로 닫힘. 바퀴마다 다시 세어지지 않게 갈래에서 **아주 빼** 여기 둔다. ⚠ 지우지는 않는다 — 되짚을 수 있어야 한다.", + "옮긴 날": "2026-09-18", + "셈": { + "basis": 10, + "form": 7, + "form_blind": 10, + "cost_rule": 7 + }, + "줄": { + "basis": { + "F0132": { + "section": "5-22-4", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(일당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "「일당」은 품의 단위(하루에 얼마)라 밑수로 안 받음", + "갈래": "거절 자리" + }, + "F0145": { + "section": "5-28-2", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(일 당)", + "master": null, + "why": "작업조 표(시공량 열이 밑수) — 마스터가 일부러 비움(c3da333e 와 같은 까닭) · 원문 「(일 당)」 은 코덱스 복원(61f35108)", + "class": "㉮ 영영 할 일 아님", + "class_why": "작업조 표 — 밑수가 시공량 열이라 일부러 비움", + "갈래": "거절 자리" + }, + "F0257": { + "section": "9-11-1", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(단위: 일당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "「일당」 · 계수표", + "갈래": "거절 자리" + }, + "F0338": { + "section": "12-6", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(단위: 일당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "작업조 표(포장공 3인·시공량)", + "갈래": "거절 자리" + }, + "F0339": { + "section": "12-7-1", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(단위: 일당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "작업조 표 — 넣으면 호표가 사라짐(2026-09-17 유로폼에서 실증)", + "갈래": "거절 자리" + }, + "F0340": { + "section": "12-7-2", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(단위: 일당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "위와 같음", + "갈래": "거절 자리" + }, + "F0341": { + "section": "12-8", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(단위: 일당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "위와 같음", + "갈래": "거절 자리" + }, + "F0395": { + "section": "12-38-3", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(단위: 일 당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "위와 같음", + "갈래": "거절 자리" + }, + "F0447": { + "section": "13-15-2", + "md": [ + 1.0, + "조" + ], + "md_evidence": "(단위: 인/조)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "「인/조」 — 「조」는 세는 단위가 아님", + "갈래": "거절 자리" + }, + "F0449": { + "section": "13-16-2", + "md": [ + 1.0, + "일" + ], + "md_evidence": "(단위: 일 당)", + "master": null, + "class": "㉮ 영영 할 일 아님", + "class_why": "위와 같음", + "갈래": "거절 자리" + } + }, + "form": { + "F0002": { + "section": "1-2-2", + "master": "reference", + "md_mark": "직공인부", + "class": "㉮ 영영 할 일 아님", + "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐", + "갈래": "그 밖" + }, + "F0007": { + "section": "1-2-7", + "master": "reference", + "md_mark": "㎥", + "class": "㉮ 영영 할 일 아님", + "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐", + "갈래": "그 밖" + }, + "F0038": { + "section": "1-4-26", + "master": "reference", + "md_mark": "적용시공량", + "class": "㉮ 영영 할 일 아님", + "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐", + "갈래": "그 밖" + }, + "F0227": { + "section": "8-6-1", + "master": "reference", + "md_mark": "유효 살포량/1회", + "why": "사람 판정 reference(Forms.py) — 기종별 희석배수별 1회 살포량 계산표", + "class": "㉮ 영영 할 일 아님", + "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐", + "갈래": "그 밖" + }, + "F0257": { + "section": "9-11-1", + "master": "coefficient", + "md_mark": "ℓ", + "class": "㉮ 영영 할 일 아님", + "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐", + "갈래": "거절 자리" + }, + "F0325": { + "section": "11-1", + "master": "reference", + "md_mark": "비계공", + "class": "㉮ 영영 할 일 아님", + "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐", + "갈래": "그 밖" + }, + "F0454": { + "section": "14-2", + "master": "reference", + "md_mark": "작업량", + "class": "㉮ 영영 할 일 아님", + "class_why": "사람 판정으로 닫힘 — 시험이 md 표지로 가리는 것과 다를 뿐", + "갈래": "부록 예시" + } + }, + "form_blind": { + "F0046": { + "section": "2-1-1", + "master": "requirement", + "why": "기계별 연료표(천공기 주연료 3ℓ·잡품 95%) — 품 낱말 없음. 같은 모양 F0044·F0045·F0047 은 사람 판정 reference 인데 이것만 requirement", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "그 밖" + }, + "F0062": { + "section": "2-1-12", + "master": "requirement", + "why": "값 칸이 「층적부피에 따라 별도계산」·「〃」 — 숫자 없음", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "원문 부재" + }, + "F0140": { + "section": "5-26-1", + "master": "requirement", + "why": "머리 소실 — 토성×깊이 숫자만 남음(사람 판정: 100㎡당 보통인부)", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "그 밖" + }, + "F0141": { + "section": "5-26-2", + "master": "requirement", + "why": "머리 소실 — 위와 같음", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "그 밖" + }, + "F0142": { + "section": "5-26-3", + "master": "requirement", + "why": "머리 소실 — 위와 같음", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "그 밖" + }, + "F0195": { + "section": "7-14", + "master": "productivity", + "why": "칸이 뭉침(「㎥ 0.06∼0.07 4.8∼6.0」) — 원문 md 가 병합 칸을 못 풂", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "그 밖" + }, + "F0237": { + "section": "8-11", + "master": "productivity", + "why": "작업량이 식 글(「Q = 3.5 ㎥/hr」)", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "그 밖" + }, + "F0342": { + "section": "12-9-1", + "master": "requirement", + "why": "수량 칸이 비어 있음 — 원문에 값 없음(「반중력식 옹벽 참조」)", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "원문 부재" + }, + "F0343": { + "section": "12-9-2", + "master": "requirement", + "why": "수량 칸이 비어 있음 — 원문에 값 없음", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "원문 부재" + }, + "F0344": { + "section": "12-9-3", + "master": "requirement", + "why": "수량 칸이 비어 있음 — 원문에 값 없음", + "class": "㉮ 영영 할 일 아님", + "class_why": "시험이 못 가리는 자리 — 값 없음·머리 소실 따위로 사람 판정에 둠", + "갈래": "원문 부재" + } + }, + "cost_rule": { + "F0369": { + "section": "12-22", + "master": "sub_requirement", + "md_rows": [ + [ + "재료비" + ], + [ + "설치비", + "재료비의 5%" + ] + ], + "class": "㉮ 영영 할 일 아님", + "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘", + "갈래": "그 밖" + }, + "F0379": { + "section": "12-29", + "master": "requirement", + "md_rows": [ + [ + "재료비", + "", + "필요수량" + ], + [ + "설치비", + "재료비의 5%" + ] + ], + "why": "⚠ 같은 꼴 6표는 reference 인데 이것만 requirement — 설치비 = 재료비의 5%", + "class": "㉮ 영영 할 일 아님", + "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘", + "갈래": "그 밖" + }, + "F0382": { + "section": "12-31", + "master": "sub_requirement", + "md_rows": [ + [ + "재료비" + ], + [ + "설치비", + "주재료비의 5%" + ] + ], + "class": "㉮ 영영 할 일 아님", + "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘", + "갈래": "그 밖" + }, + "F0383": { + "section": "12-32", + "master": "sub_requirement", + "md_rows": [ + [ + "재료비", + "P.V.C ∅54m/m" + ], + [ + "설치비", + "주재료비의 10%" + ] + ], + "class": "㉮ 영영 할 일 아님", + "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘", + "갈래": "그 밖" + }, + "F0386": { + "section": "12-34-2", + "master": "sub_requirement", + "md_rows": [ + [ + "재료비", + "1회 기준 46.1%" + ], + [ + "노무비", + "1회 기준 47.1%" + ] + ], + "class": "㉮ 영영 할 일 아님", + "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘", + "갈래": "그 밖" + }, + "F0388": { + "section": "12-34-4", + "master": "sub_requirement", + "md_rows": [ + [ + "재료비", + "JOINT FILLER" + ] + ], + "class": "㉮ 영영 할 일 아님", + "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘", + "갈래": "그 밖" + }, + "F0390": { + "section": "12-36", + "master": "sub_requirement", + "md_rows": [ + [ + "제작비", + "견적처리" + ], + [ + "운송비", + "견적처리" + ], + [ + "설치비", + "견적처리" + ] + ], + "class": "㉮ 영영 할 일 아님", + "class_why": "비목 구성표 — `sub_requirement` 로 판정이 닫힘", + "갈래": "그 밖" + } + } + }, + "갈래별": { + "거절 자리": 11, + "그 밖": 18, + "부록 예시": 1, + "원문 부재": 4 + } } } diff --git a/resources/tester/test_base_data_source_match.py b/resources/tester/test_base_data_source_match.py index 772c3ab3..d0f8c2ae 100644 --- a/resources/tester/test_base_data_source_match.py +++ b/resources/tester/test_base_data_source_match.py @@ -513,7 +513,16 @@ def current_mismatches(src: dict) -> dict[str, dict]: def load_known() -> dict[str, dict]: - return json.loads(KNOWN.read_text(encoding="utf-8")) + """알려진 어긋남 목록. **「할 일 아님」 칸(`_할일_아님`)도 함께 읽는다.** + + ⚠ 왜 합쳐 읽나 — ㉮(원문에 없음·거절 자리·판정으로 닫힘)는 **바퀴마다 다시 세어지지 않게** + 갈래에서 빼 별도 칸에 둔다(2026-09-18 브레인). 그렇다고 비교에서까지 빠지면 그 줄이 다시 + 「새로 어긋남」으로 빨개진다 — 그래서 **세는 곳에서만 빼고 비교에는 넣는다.** + """ + data = json.loads(KNOWN.read_text(encoding="utf-8")) + for kind, rows in ((data.get("_할일_아님") or {}).get("줄") or {}).items(): + data.setdefault(kind, {}).update(rows) + return data @pytest.fixture(scope="module") diff --git a/resources/tester/test_base_prices_source_match.py b/resources/tester/test_base_prices_source_match.py index cd50179d..e3ab6501 100644 --- a/resources/tester/test_base_prices_source_match.py +++ b/resources/tester/test_base_prices_source_match.py @@ -624,7 +624,16 @@ def current_mismatches(src: dict) -> dict[str, dict]: def load_known() -> dict[str, dict]: - return json.loads(KNOWN.read_text(encoding="utf-8")) + """알려진 어긋남 목록. **「할 일 아님」 칸(`_할일_아님`)도 함께 읽는다.** + + ⚠ 왜 합쳐 읽나 — ㉮(원문에 없음·거절 자리·판정으로 닫힘)는 **바퀴마다 다시 세어지지 않게** + 갈래에서 빼 별도 칸에 둔다(2026-09-18 브레인). 그렇다고 비교에서까지 빠지면 그 줄이 다시 + 「새로 어긋남」으로 빨개진다 — 그래서 **세는 곳에서만 빼고 비교에는 넣는다.** + """ + data = json.loads(KNOWN.read_text(encoding="utf-8")) + for kind, rows in ((data.get("_할일_아님") or {}).get("줄") or {}).items(): + data.setdefault(kind, {}).update(rows) + return data # ── 시험 ──────────────────────────────────────────────────────────────── diff --git a/resources/tester/test_work_item_master_source_match.py b/resources/tester/test_work_item_master_source_match.py index 5a07be85..9acbc363 100644 --- a/resources/tester/test_work_item_master_source_match.py +++ b/resources/tester/test_work_item_master_source_match.py @@ -326,18 +326,52 @@ def current_mismatches(src: dict) -> dict[str, dict]: def load_known() -> dict[str, dict]: - return json.loads(KNOWN.read_text(encoding="utf-8")) + """알려진 어긋남 목록. **「할 일 아님」 칸(`_할일_아님`)도 함께 읽는다.** + + ⚠ 왜 합쳐 읽나 — ㉮(원문에 없음·거절 자리·판정으로 닫힘)는 **바퀴마다 다시 세어지지 않게** + 갈래에서 빼 별도 칸에 둔다(2026-09-18 브레인). 그렇다고 비교에서까지 빠지면 그 줄이 다시 + 「새로 어긋남」으로 빨개진다 — 그래서 **세는 곳에서만 빼고 비교에는 넣는다.** + """ + data = json.loads(KNOWN.read_text(encoding="utf-8")) + for kind, rows in ((data.get("_할일_아님") or {}).get("줄") or {}).items(): + data.setdefault(kind, {}).update(rows) + return data #: 비교에서 뺄 칸 — **사람이 적은 메모**다. 자료가 아니라 설명이라 어긋남 판정에 안 쓴다. #: `class`·`class_why` 는 2026-09-18 에 붙인 갈래(㉮ 영영 아님 · ㉯ 남의 몫 · ㉰ 사용자 판단). -_MEMO_FIELDS = ("why", "class", "class_why", "was", "owner", "owner_ask") +_MEMO_FIELDS = ("why", "class", "class_why", "was", "owner", "owner_ask", "갈래") def _without_why(entry: dict) -> dict: return {k: v for k, v in entry.items() if k not in _MEMO_FIELDS} +def test_할일_아님_칸이_갈려_있다() -> None: + """㉮ — **영영 할 일이 아닌 것**은 갈래에서 빼 별도 칸에 둔다(2026-09-18 브레인). + + ⚠ 왜 못박나 — 섞여 있으면 바퀴마다 「어긋남」으로 다시 세어져 **없는 할 일이 생긴다.** + ⚠ 지우지는 않는다 — 나중에 「이거 왜 안 했지」 할 때 사유를 보아야 한다. + """ + block = json.loads(KNOWN.read_text(encoding="utf-8")).get("_할일_아님") + assert block, "「할 일 아님」 칸이 없다" + rows = block["줄"] + total = sum(len(v) for v in rows.values()) + assert total == 34 + assert block["갈래별"] == {"거절 자리": 11, "그 밖": 18, "부록 예시": 1, "원문 부재": 4} + for kind, got in rows.items(): + for tid, v in got.items(): + assert str(v.get("class", "")).startswith("㉮"), f"{kind}/{tid}" + assert v.get("class_why"), f"{kind}/{tid} 사유 없음" # 왜 할 일이 아닌지 + assert v.get("갈래") in ("원문 부재", "부록 예시", "거절 자리", "그 밖"), ( + f"{kind}/{tid}" + ) + # 세는 칸에서는 빠지고 비교에는 들어간다 — 그래서 빨개지지 않는다 + merged = load_known() + for kind, got in rows.items(): + assert set(got) <= set(merged[kind]) + + # ── 시험 ──────────────────────────────────────────────────────────────── def test_표_수와_자리(src: dict) -> None: """원문 표가 원본(`pum_forest`)에 같은 차례·같은 줄로 있고, 마스터에 빠짐없이 한 번씩 있음."""