From 490cab5e2c86f8f80f629dfb7343e01ee2f6ddac Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 15 Sep 2026 05:18:16 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix(b09):=20=EC=82=AC=EC=9C=A0=20=EC=97=86?= =?UTF-8?q?=EC=9D=B4=20=EB=B2=84=EB=A0=A4=EC=A7=80=EB=8D=98=20=EC=9E=8E=20?= =?UTF-8?q?=EA=B3=B5=EC=A2=85=2049=20=EC=97=90=20=EC=82=AC=EC=9C=A0=20?= =?UTF-8?q?=E2=80=94=20=ED=91=9C=20=EB=B2=88=ED=98=B8=C2=B7=EB=AA=A8?= =?UTF-8?q?=EC=96=91(=EC=9E=91=EC=97=85=EB=9F=89=C2=B7=EC=86=8C=EC=9A=94?= =?UTF-8?q?=EB=9F=89=C2=B7=ED=98=95=EC=8B=9D=20=EB=AF=B8=EC=A0=95)=C2=B7?= =?UTF-8?q?=ED=95=9C=20=EC=A4=84=EB=8F=84=20=EC=95=88=20=EC=84=AC=EC=9D=84?= =?UTF-8?q?=20=EC=84=B1=EB=B6=84=20=EB=AF=B8=ED=99=95=EB=B3=B4=EB=A1=9C(?= =?UTF-8?q?=EC=9D=BD=EB=8A=94=20=EA=B8=B8=EC=9D=80=20=EC=95=88=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A6=20=C2=B7=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=ED=95=B4=EB=8B=B9=200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- B09_Estimation/B09_Estimation_UnitPrice.py | 4 ++ B09_Estimation/B09_Estimation_UnreadTables.py | 42 ++++++++++++++ resources/tester/test_b09_unread_tables.py | 55 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 B09_Estimation/B09_Estimation_UnreadTables.py create mode 100644 resources/tester/test_b09_unread_tables.py diff --git a/B09_Estimation/B09_Estimation_UnitPrice.py b/B09_Estimation/B09_Estimation_UnitPrice.py index 1f8442c6..496693b3 100644 --- a/B09_Estimation/B09_Estimation_UnitPrice.py +++ b/B09_Estimation/B09_Estimation_UnitPrice.py @@ -1134,6 +1134,10 @@ def build_unit_prices( from B09_Estimation.B09_Estimation_RockLoss import attach_rock_loss attach_rock_loss(build, master) + # 제목도 사유도 없는 잎 공종 — 버린 표 번호·모양을 사유로(2026-09-15 · 모든 공종 모듈이 돈 뒤). + from B09_Estimation.B09_Estimation_UnreadTables import mark_unread_tables + + mark_unread_tables(build, master) build.labor_reliability = _labor_reliability_in_use(build.book) # ③ 할증 포함 재료량을 준 공종에 자재가 재료비로 붙지 않았는가(명세 6장 · ㉠). from B09_Estimation.B09_Estimation_Guards import check_materials_before_surcharge diff --git a/B09_Estimation/B09_Estimation_UnreadTables.py b/B09_Estimation/B09_Estimation_UnreadTables.py new file mode 100644 index 00000000..57ee2184 --- /dev/null +++ b/B09_Estimation/B09_Estimation_UnreadTables.py @@ -0,0 +1,42 @@ +"""B09 원가계산 — **사유 없이 버려지는 표에 사유를 남긴다** (2026-09-15 브레인 「버릴 때는 사유」). + +일할 표(작업량·소요량·형식 미정)를 가진 잎 공종인데 제목도 사유도 없는 것 — 표를 읽는 길이 없어 한 줄도 +안 서고 어디에도 안 남음(잰 수 49 · ㉯ 가르기에서 먼저 6). 내역서가 「일위대가가 아직 없습니다」 만 띄워 +무엇을 손볼지 모름 → 표 번호·모양을 `component_gaps` 에 적음(내역서 「성분 미확보 — …」 로 섬). +⚠ 읽는 길을 만드는 것이 아님 — 그 표가 풀리면 제목이 서서 이 사유는 저절로 안 붙음. +""" + +from __future__ import annotations + +from typing import Any + +#: 마스터 표 형식 → 사유에 쓰는 이름. 여기 없는 형식(참고·계수)은 일할 표가 아님. +FORM_LABELS = { + "productivity": "작업량 표", + "requirement": "소요량 표", + "undetermined": "형식 미정 표(모양 판정 안 됨)", +} +UNREAD = "표를 읽는 길이 없어 한 줄도 안 섬 — {tables}" + + +def mark_unread_tables(build: Any, master: dict[str, Any]) -> None: + """제목·사유가 둘 다 없는 잎 공종에 표 번호·모양 사유를 단다.""" + from B09_Estimation.B09_Estimation_KnownGaps import known_gap_note + + nodes = master.get("work_items", []) + parents = {node.get("parent_code") for node in nodes} + titled = {code[2:].split("#")[0] for code in build.book.titles if code.startswith("B-")} + for node in nodes: + code = node["work_item_code"] + tables = [t for t in node.get("tables") or [] if t.get("pum_form") in FORM_LABELS] + if not tables or code in titled or code in parents: + continue + if ( + build.unattached.get(code) + or build.component_gaps.get(code) + or code in build.basis_missing + or known_gap_note(code) + ): + continue + listed = " · ".join(f"{t.get('pum_table_id')} {FORM_LABELS[t['pum_form']]}" for t in tables) + build.component_gaps[code] = UNREAD.format(tables=listed) diff --git a/resources/tester/test_b09_unread_tables.py b/resources/tester/test_b09_unread_tables.py new file mode 100644 index 00000000..f4f246bc --- /dev/null +++ b/resources/tester/test_b09_unread_tables.py @@ -0,0 +1,55 @@ +"""사유 없이 버려지는 표 — 2026-09-15 브레인 차례 ① (㉯ 가르기에서 6 · 전체로 재면 49). + +일할 표(작업량·소요량·형식 미정)를 가진 잎 공종인데 **제목도 사유도 없는** 것 — 표를 읽는 길이 없어 +한 줄도 안 서고 `unattached`·`component_gaps`·밑수·[주] 사유 어디에도 안 남아, 내역서엔 「일위대가가 아직 +없습니다」 만 뜸(무엇을 손볼지 모름). 예: 3-1 경계표시 F0075 「0.2 | 보통인부」(값이 이름 앞) · +6-4-1 덩굴걷기 F0158 · 7-3 아키야윈치 F0174 · 10-13-1 드론운반 F0322(형식 미정). +⇒ 읽는 길을 만드는 것은 뒤(예취기 한 모양 4 …) — 여기서는 **버린 표를 사유로 남김**(버릴 때는 사유). +""" + +from __future__ import annotations + +from B09_Estimation.B09_Estimation_KnownGaps import known_gap_note +from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master +from B09_Estimation.B09_Estimation_UnitPrice import cached_build + +WORK_FORMS = {"productivity", "requirement", "undetermined"} + + +def test_제목_없는_잎_공종은_모두_사유가_있음() -> None: + build = cached_build() + master = load_work_item_master() + parents = {n.get("parent_code") for n in master["work_items"]} + titled = {t[2:].split("#")[0] for t in build.book.titles if t.startswith("B-")} + silent = [ + n["work_item_code"] + for n in master["work_items"] + if n["work_item_code"] not in titled + and n["work_item_code"] not in parents + and any(t.get("pum_form") in WORK_FORMS for t in n.get("tables") or []) + and not ( + build.unattached.get(n["work_item_code"]) + or build.component_gaps.get(n["work_item_code"]) + or n["work_item_code"] in build.basis_missing + or known_gap_note(n["work_item_code"]) + ) + ] + assert silent == [], silent + + +def test_사유는_표_번호와_모양을_가리킴() -> None: + gaps = cached_build().component_gaps + for code, table in ( + ("FP-03-01", "F0075"), + ("FP-06-04-01", "F0158"), + ("FP-07-03", "F0174"), + ("FP-03-08", "F0082"), + ): + assert table in gaps[code] and "한 줄도" in gaps[code], (code, gaps.get(code)) + assert "F0322" in gaps["FP-10-13-01"] and "형식 미정" in gaps["FP-10-13-01"] + + +def test_부모와_제목_선_공종에는_안_붙음() -> None: + build = cached_build() + assert "FP-04-01" not in build.component_gaps # choose_one 부모 — 잎이 섬 + assert "FP-05-24-01" not in build.component_gaps # 제목 선 공종 From 607e7c0cd88599603e910d08622accb1a66bda16 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 15 Sep 2026 05:33:34 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix(b09):=20=EB=AC=B6=EC=9D=8C=20=EC=A4=84?= =?UTF-8?q?=20=EA=B8=88=EC=95=A1=EC=9D=B4=20=EC=A4=84=20=EC=88=98=EB=9F=89?= =?UTF-8?q?=EB=A7=8C=ED=81=BC=20=EB=B6=80=ED=92=80=EB=8D=98=20=EA=B2=83=20?= =?UTF-8?q?=E2=80=94=20=EC=A1=B0=EA=B0=81=20=EC=88=98=EB=9F=89=EC=9D=80=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=20=ED=95=A9=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 까닭 — B08 인계의 조각 수량은 「합계」인데(그쪽 시험이 지킴: BOX 동바리 3.92×10 · 포장 97.66㎥) B09 가 Σ(조각 단가 × 조각 수량) 을 1단위 단가로 보고 줄 수량을 또 곱했음. 평떼 718.14㎡ 가 720배(42억)로 섰음(2026-09-15 랩탑 서브 실측). 고침 — 계약은 그대로 두고 읽는 쪽을 고침. 합계를 그대로 금액으로 쓰고 단가는 나눠서 보임. 재·노·경 성분 셋도 같이. 계약을 코드 가까이 못박음 — 두 자리(composite_parts 칸 · _composite_row). ⭐ 시험도 고침 — 종전이 줄 수량 1 로만 재서 못 잡았음. _line(parts, quantity) 로 열고 수량 2·10·718.14 로 잼 · 성분 셋도 따로. 빨강 4 확인한 뒤 고쳤음. ⚠ 지금 검증 프로젝트엔 값이 선 묶음 줄이 0 이라 금액 변화는 안 드러남 — BOX·포장·집수정·녹화가 서면 그때 제 값으로 섬. 전체 시험 2136 통과 · 28 건너뜀 · 1 xfail · 1 실패는 내 변경과 무관한 노임 신뢰도 시험(아래 보고). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Prk9BCHG1EMAywk9k8wegA --- .../B09_Estimation_BillOfQuantities_Input.py | 5 +++ .../B09_Estimation_BillOfQuantities_Rows.py | 22 +++++++---- .../test_b09_composite_partial_parts.py | 39 +++++++++++++++++-- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/B09_Estimation/B09_Estimation_BillOfQuantities_Input.py b/B09_Estimation/B09_Estimation_BillOfQuantities_Input.py index 8731689e..1b2ae559 100644 --- a/B09_Estimation/B09_Estimation_BillOfQuantities_Input.py +++ b/B09_Estimation/B09_Estimation_BillOfQuantities_Input.py @@ -55,6 +55,11 @@ class HandoffWorkItem: application_ratio_breakdown: dict | None = None quantity_breakdown: dict | None = None #: 묶음 줄(옹벽처럼 품셈에 그 공종이 없는 것) — 무엇으로 이루어지는지. + #: ⭐ **조각 수량은 「합계」다 — 1단위당이 아니다.** B08 과의 계약이고 그쪽 시험이 지킨다 + #: (BOX 동바리 3.92 × 연장 10m · 포장 97.66㎥). 그러므로 `Σ(조각 단가 × 조각 수량)` 이 + #: **그 줄 전체 금액**이며 **줄 수량을 다시 곱하면 안 된다**. + #: ⚠ 이 줄이 안 적혀 있어서 금액이 배로 부풀었다 — 평떼 718.14㎡ 가 720배(42억). + #: 2026-09-15 랩탑 서브 실측 · 고친 자리는 `_Rows._composite_row`. composite_parts: tuple = () #: 묶음인데 아직 못 채운 조각 — 「단가 없음」과 「물량 없음」을 갈라 적는다. composite_not_ready: tuple = () diff --git a/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py b/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py index 318a731a..dee8ee3e 100644 --- a/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py +++ b/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py @@ -171,15 +171,23 @@ def _composite_row( ) return row + # ⭐ **인계 조각 수량은 「합계」다** — B08 과의 계약이고 그쪽 시험이 지킨다 + # (BOX 동바리 3.92 × 연장 10m · 포장 97.66㎥). 그래서 위에서 더한 `money` 는 이미 + # **그 줄 전체 금액**이지 1단위 단가가 아니다. 종전엔 이것을 1단위로 보고 아래에서 + # 줄 수량을 **한 번 더** 곱해 금액이 배로 부풀었다 — 평떼 718.14㎡ 가 720배(42억)로 섰다 + # (2026-09-15 랩탑 서브 실측 · B09 시험이 줄 수량 1 로만 재서 못 잡았음). + # 계약은 그대로 두고 **읽는 쪽**을 고친다 — 합계를 그대로 쓰고 단가는 나눠서 보인다. money = money.floored(Decimal(1)) - line = bill_line(money, settle_quantity(row)) + quantity = settle_quantity(row) _set_unit(row, money) - row.unit_price_krw = round_at(money.total, OutputPlace.UNIT_PRICE_ROW) - row.amount_krw = line.total - row.material_krw = line.material - row.labor_krw = line.labor - row.expense_krw = line.expense - row.add_note("quantity", f"묶음 {len(item.composite_parts)}조각 합계") + row.unit_price_krw = ( + round_at(money.total / quantity, OutputPlace.UNIT_PRICE_ROW) if quantity else None + ) + row.amount_krw = money.total + row.material_krw = money.material + row.labor_krw = money.labor + row.expense_krw = money.expense + row.add_note("quantity", f"묶음 {len(item.composite_parts)}조각 합계 — 조각 수량이 이미 합계") _mark_manual_materials(row, [code for code, _ in row.parts], unit_prices, result) return row diff --git a/resources/tester/test_b09_composite_partial_parts.py b/resources/tester/test_b09_composite_partial_parts.py index 559363cb..b2a8bf2e 100644 --- a/resources/tester/test_b09_composite_partial_parts.py +++ b/resources/tester/test_b09_composite_partial_parts.py @@ -7,11 +7,13 @@ from __future__ import annotations +import pytest + from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill from B09_Estimation.B09_Estimation_UnitPrice import cached_build -def _composite(parts: list[dict]) -> dict: +def _composite(parts: list[dict], quantity: str = "1") -> dict: return { "work_items": [ { @@ -19,7 +21,7 @@ def _composite(parts: list[dict]) -> dict: "name": "시험 묶음", "spec": "", "unit": "개소", - "quantity": "1", + "quantity": quantity, "in_bill": True, "composite_parts": parts, "composite_not_ready": [], @@ -29,8 +31,8 @@ def _composite(parts: list[dict]) -> dict: } -def _line(parts: list[dict]): - rows = build_bill(_composite(parts)).rows +def _line(parts: list[dict], quantity: str = "1"): + rows = build_bill(_composite(parts, quantity)).rows return next(r for r in rows if r.code is None and r.name == "시험 묶음") @@ -52,3 +54,32 @@ def test_밑수_없는_조각도_같이_막음() -> None: def test_온전한_조각만이면_종전대로_금액() -> None: line = _line([{"code": "FP-09-15-02", "quantity": 2}]) assert line.amount_krw and line.amount_krw > 0, line.note + + +# ── 줄 수량이 1 이 아닐 때 — 2026-09-15 랩탑 서브가 찾은 부풀음 ── +#: ⭐ **인계 조각 수량은 「합계」다** — B08 계약이고 그쪽 시험이 지킴 +#: (BOX 동바리 3.92×10 · 포장 97.66㎥). +#: 그래서 `Σ(조각 단가 × 조각 수량)` 이 이미 **그 줄 전체 금액**이다. +#: 줄 수량을 또 곱하면 배로 부푼다. 종전 시험이 **줄 수량 1 로만 재서** 못 잡았다 — +#: 평떼 718.14㎡ 가 720배(42억)로 섰다. +_PART = {"code": "FP-09-15-02", "quantity": 2} + + +@pytest.mark.parametrize("quantity", ["2", "10", "718.14"]) +def test_묶음_금액은_줄_수량을_다시_안_곱한다(quantity) -> None: + """조각이 합계이므로 **줄 수량이 몇이든 금액은 같다**. 단가는 그 금액 ÷ 수량으로 보인다.""" + one = _line([_PART]) + many = _line([_PART], quantity) + assert one.amount_krw and many.amount_krw, (one.amount_krw, many.amount_krw) + assert many.amount_krw == one.amount_krw, (quantity, many.amount_krw, one.amount_krw) + assert many.unit_price_krw < one.unit_price_krw, (quantity, many.unit_price_krw) + + +def test_묶음_성분도_줄_수량만큼_안_부푼다() -> None: + """재·노·경 셋도 같이 봐야 함 — 합계만 보면 성분이 부푼 것을 놓친다.""" + one, many = _line([_PART]), _line([_PART], "10") + assert (many.material_krw, many.labor_krw, many.expense_krw) == ( + one.material_krw, + one.labor_krw, + one.expense_krw, + ) From 7bc97f062a7674224dbd60acab8e556ecbb4e33b Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 15 Sep 2026 05:34:14 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore(plan):=20=EB=9E=A9=ED=83=91=20?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=E2=80=94=20=EC=82=AC=EC=9C=A0=20=EC=97=86?= =?UTF-8?q?=EB=8D=98=20=EC=9E=8E=20=EA=B3=B5=EC=A2=85=2049=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=EC=99=84=EB=A3=8C=20=C2=B7=20=EB=82=A8=EC=9D=80=20?= =?UTF-8?q?=EC=B0=A8=EB=A1=80=20=EC=98=88=EC=B7=A8=EA=B8=B0=20=ED=95=9C=20?= =?UTF-8?q?=EB=AA=A8=EC=96=91=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- PLAN_랩탑메인.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PLAN_랩탑메인.md b/PLAN_랩탑메인.md index 057e0dc3..58247073 100644 --- a/PLAN_랩탑메인.md +++ b/PLAN_랩탑메인.md @@ -7,13 +7,14 @@ ### 남은 것 - [ ] ⚠ 자원 이름이 겹치면 남의 공종 풀이가 바뀜(명세 2장 이름 맞춤의 부작용) — 자원 목록에 「양수기」 를 넣자 12-26 물푸기 「양수기(150m/m)」 줄 풀이가 바뀌어 제목이 사라짐 → 지금은 「유인헬기방제 양수기」 로 피함(임시) · 언젠가 공종 범위·코드로 맞출 자리 -- [ ] ㉯ 나머지 — 제목 없음(지금 셈 34 · 같은 자) 가르기: ⓐ 참고 표 19(1-2 단위표준·운반 · 2장 소요재료·기계손료 17 — 다른 공종이 읽는 표라 제목이 안 서는 게 맞음) ⓑ choose_one 부모 2(4-1·4-2 — 자식이 섬 · 단어는 부록 설계예시표 F0475·F0476 에서 걸림) ⓒ 단어는 설계예시표에서만 · 자기 표는 인력뿐 3(3-1 경계표시 **사유 없음** · 3-2 작업로 선정 초급기술자 못 풂 · 3-3 작업로 설치 비율 인력 못 풂) ⓓ **예취기·분무기 인력 표 한 모양 4**(6-2-2 줄베기 · 6-2-3 모두베기 · 6-4-1 덩굴걷기 **사유 없음** · 6-4-2 덩굴 약제 — 「이름 | 도구 | 인/ha | 값 | 직종」 줄을 못 읽음 · 풀리면 2장 예취기·배부식분무기 1일 몫을 체인톱처럼) ⓔ 이름에만 드론·윈치 3(3-8 드론 영상 촬영 인력만 **사유 없음** · 7-3 아키야윈치 **사유 없음** · 8-6-2 드론방제 인력 줄 못 풂) ⓕ 거리 표 2(10-13-1 드론운반 · 10-13-2 인력운반 — 형식 미정으로 건너뜀 · **사유 없음**) ⓖ 13-10-2 나무 말뚝박기 1(뭉친 칸) · 사유 없이 버려지는 표 6(F0075 · F0082 · F0158 · F0174 · F0322 · F0323) +- [ ] ㉯ 나머지 — 제목 없음(지금 셈 34 · 같은 자) 가르기: ⓐ 참고 표 19(1-2 단위표준·운반 · 2장 소요재료·기계손료 17 — 다른 공종이 읽는 표라 제목이 안 서는 게 맞음) ⓑ choose_one 부모 2(4-1·4-2 — 자식이 섬 · 단어는 부록 설계예시표 F0475·F0476 에서 걸림) ⓒ 단어는 설계예시표에서만 · 자기 표는 인력뿐 3(3-1 경계표시 **사유 없음** · 3-2 작업로 선정 초급기술자 못 풂 · 3-3 작업로 설치 비율 인력 못 풂) ⓓ **예취기·분무기 인력 표 한 모양 4**(6-2-2 줄베기 · 6-2-3 모두베기 · 6-4-1 덩굴걷기 **사유 없음** · 6-4-2 덩굴 약제 — 「이름 | 도구 | 인/ha | 값 | 직종」 줄을 못 읽음 · 풀리면 2장 예취기·배부식분무기 1일 몫을 체인톱처럼) ⓔ 이름에만 드론·윈치 3(3-8 드론 영상 촬영 인력만 **사유 없음** · 7-3 아키야윈치 **사유 없음** · 8-6-2 드론방제 인력 줄 못 풂) ⓕ 거리 표 2(10-13-1 드론운반 · 10-13-2 인력운반 — 형식 미정으로 건너뜀 · **사유 없음**) ⓖ 13-10-2 나무 말뚝박기 1(뭉친 칸) · 사유 없이 버려지던 것은 전체 49 로 넓혀 사유 붙음(490cab5e) · 남은 차례: 예취기 한 모양 4 → 나머지(3-2 · 3-3 · 8-6-2 · 13-10-2) - [ ] 막힌 셋 풀기(원문에 값이 없음 · 입력·견적 칸이 서야 함): 12-26 물푸기 가동시간 · 10-7-4 모노레일 ps·임대료·운반거리 · 10-8-3 케이블크레인 ℓ/kWh · 8-6-3 지상방제 1일 작업량·1톤 트럭 - [ ] 손료계수 없는 기계 222종 되살림(미룸 · 브레인 승인 — 지금 막는 공종 0) — 가르기 끝: 원문 없음 0 · 원천 한 줄 누락 1(3201-0003 「-0003」 표기) · 뭉친 칸이지만 열마다 개수 맞고 상각+정비+관리=계 검증 통과 196(크레인(무한궤도) 15 · 골재생산(컨베이어·크러셔·스크린·빈) 55 · 해상(준설선·예선·대선 등) 66 · 그 밖 60) · 글줄로 붙음 25(3601 콘크리트 피니셔 5 · 9020 준설선 7 · 9060 토운선 6 · 7) · 원문 md 한 줄 읽기가 기존 계수 383/383 일치 · 8-4 운전경비 버려진 줄 3 = 81기종(포장 27 · 기타 36 · 기타 18) 중 공종이 쓰는 것은 물탱크 하나(되살림 끝) - [ ] 10-7-4 모노레일 제목의 보통인부 두 번 셈(4인) — 막힌 채 사유만 적음 · 풀리는 날 운전 단가표 2인을 빼고 셀 것 - [ ] 소형 장비 1일 소모 표(㉯) 가운데 2-1-2 경유 표(집재장비) · 천공기 · 예취기 — 쓰는 공종이 풀릴 때 같은 한 벌로 ### 검증완료(2026-09-14 밤 ~ 09-15 · 커밋 해시) +- [X] 사유 없이 버려지던 잎 공종 49 에 사유(브레인 차례 ① · ㉯ 자로 본 6 을 전체로 넓혀 셈) — `B09_Estimation_UnreadTables`: 일할 표(작업량·소요량·형식 미정)가 있는데 제목도 사유(못 붙은 줄·성분 빠짐·밑수·[주])도 없는 잎 공종에 「표를 읽는 길이 없어 한 줄도 안 섬 — 표 번호 모양」 을 성분 미확보로 · 읽는 길은 안 만듦(그 표가 풀리면 저절로 안 붙음) · 전후: 사유 +49 · 제목·금액 변화 0 · 프로젝트 내역에 뜨는 것 0(인계 24 공종과 안 겹침 — 화면 묶어 보이기는 아직 필요 없음) · 49 = 2-1-12 · 3장 3 · 4장 4 · 5장 6(5-26 흙갈이 셋은 형식 미정) · 6장 3 · 7장 14 · 8장 9 · 9장 4(콘크리트 깨기) · 10장 4 · 12-29 1 = 이 목록이 곧 「읽는 길」 차례표. 검증완료(490cab5e) - [X] 조종원 직종 8-1-2 5호 원문대로(브레인 「지금 끼울 것」) — 원문이 이름·규격으로 가르는 기종만(덤프트럭 12t · 콘크리트 믹서 0.55㎥ · 공기압축기(이동식) 2.83㎥/min · 괄호 앞 이름 끝말로 · 살수차는 괄호 속) · 바뀐 레코드 정확히 12(덤프 15·20·24·32t 화물차→건설기계 · 콘크리트 믹서 0.10~0.45 · 래머 · 플레이트 콤팩터 → 일반기계) · 원문으로 가름 161 · 잠정 56(콘크리트 펌프차 · 파일천공전용장비 · 타워크레인 · 트랙터(타이어) · 크러셔 · 크롤러드릴 …) 은 옛 규칙 + 중기경비 장 사유 · 곁: 파쇄기 중기경비 장이 연료·조종원 없다고 거짓 공백을 띄우던 것 걷음 · 전후(본당): 9-14-2 14,523 → 9,177 · 10-12 L164.23m 3,249 → 3,519 · 9,172 → 9,912 · 10,520 → 11,369 · 프로젝트 본체 109,671,600 → 109,682,519(+10,919 · 덤프 운반 토사) · 거창 실무 「덤프 15Ton 건설기계 · 8·4.5Ton 화물차 · 플레이트 콤팩터 일반기계운전사」 = 원문. 검증완료(22692669) - [X] 초류종자살포 5-24-1·2 둘이 섬(브레인 판정 ①②′④) — ① 트럭 4.5ton → 덤프트럭 4.5(범위 별칭 · 옆 칸 규격이 대상 규격과 같으면 덮는 것 아님) ②′ 종자살포기 → 취부기 11.94㎾ 손료만(`LOSS_ONLY_MACHINES` · 8-4 에 줄 없음 · 거창도 손료 20,295 만 · 디젤엔진은 원문 둘 다에 없어 안 넣음) ④ 종자 후보 넷 칸 · 넣은 하나만 · 둘이면 사유(`B09_Estimation_SeedSpray`) · 어긋남 사유 나란히 · 233 → 527(③) → 744원/㎡(재료 단가 전) · 다른 공종·프로젝트 내역 변화 0. 검증완료(3d896dec) - [X] ③ 물탱크(살수차) 운전경비 되살림 — 8-4-8 셀 병합으로 줄째 버려지던 7204 다섯 원문 값(8.2·8.6·9.3·9.4·12.9ℓ · 잡재료 30 · 1인) · 조종원 8-1-2 5호 살수차 = 화물차운전사 · 거창 5,500ℓ 일치. 검증완료(078af37d)