From a2b3ae698f270742e1d053182aaba9ecc00c28ce Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 08:57:24 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix(test):=20=EB=8D=A4=ED=94=84=20=EC=A0=81?= =?UTF-8?q?=EC=9E=AC=20=EB=92=A4=20=EB=82=A1=EC=9D=80=20=EC=8B=9C=ED=97=98?= =?UTF-8?q?=20=EB=91=98=20=E2=80=94=20=EA=B0=80=EB=93=9C=20=EB=9C=BB?= =?UTF-8?q?=EC=9D=80=20=EC=A7=80=ED=82=A4=EA=B3=A0=20=EC=83=88=20=EB=AA=A8?= =?UTF-8?q?=EC=96=91=EC=97=90=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - v16 「덤프 운반은 아직 안 선다」가 지키던 것 = 운반거리(설계 입력) 없이 운반이 조용히 금액을 갖지 않기 → 「거리가 와야 선다」로: 거리 없는 기본 조립엔 운반(#L…m) 없음 · 거리 무관 적재(#적재)만 섬 - 갈래 제목 ⊂ 마스터 갈래 키: 10-12 는 표가 아니라 본문 식이라 마스터 표에 갈래가 없음 (#L…m 는 프로젝트 거리마다 생겨 마스터에 둘 수도 없음) → 식이 내는 FP-10-12 갈래만 까닭 적고 제외 - 58afe8f2 를 일부 시험만 돌리고 올려 난 빨강 · 전체 1763 통과 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- resources/tester/test_b09_verify_guards.py | 10 +++++++--- resources/tester/test_work_item_master_variants.py | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/resources/tester/test_b09_verify_guards.py b/resources/tester/test_b09_verify_guards.py index 941371ef..b82f081a 100644 --- a/resources/tester/test_b09_verify_guards.py +++ b/resources/tester/test_b09_verify_guards.py @@ -53,10 +53,14 @@ def test_v16_불도저_운반이_갈래마다_금액으로_선다() -> None: assert build.book.resolve(code).total > 0, code -def test_v16_덤프_운반은_아직_안_선다() -> None: - """⚠ 안 서는 것도 사실대로 못 박는다 — 운반거리(설계 입력)를 기다리는 자리다.""" +def test_v16_덤프_운반은_거리가_와야_선다() -> None: + """⚠ 안 서는 것도 사실대로 못 박는다 — 운반은 운반거리(설계 입력)가 와야 선다. + + 2026-09-14 — 적재(10-12 「1. 적재」)는 거리 무관이라 늘 서고, 운반(`#L…m`)은 거리가 온 조립에만. + """ build = cached_build() - assert not [code for code in build.book.titles if code.startswith("B-FP-10-12")] + dump = [code for code in build.book.titles if code.startswith("B-FP-10-12")] + assert dump and all(code.endswith("#적재") for code in dump) # ── V-17 ──────────────────────────────────────────────────────────── diff --git a/resources/tester/test_work_item_master_variants.py b/resources/tester/test_work_item_master_variants.py index 3e0983c8..9a46aa3d 100644 --- a/resources/tester/test_work_item_master_variants.py +++ b/resources/tester/test_work_item_master_variants.py @@ -23,6 +23,10 @@ def test_단가표_갈래_제목은_전부_마스터_갈래_키에_있다() -> N for code in cached_build().book.titles: if not code.startswith("B-") or "#" not in code: continue + if code.startswith("B-FP-10-12-"): + # 덤프 운반·적재(10-12)는 **표가 아니라 본문 식**이라 마스터 표에 갈래가 없음 — + # 갈래(`#적재`·`#L…m`)는 식이 냄(2026-09-14 · `MachineProductivity_Dump`). + continue work_item, variant = code[2:].split("#", 1) keys = { normalize_variant_key(k) for k in nodes.get(work_item, {}).get("variant_keys") or [] From a440b4f9190f8198bd18aca185a0db59d86f3c4b Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 08:57:24 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat(b08):=20=EB=8D=A4=ED=94=84=20=EC=9A=B4?= =?UTF-8?q?=EB=B0=98=EB=A7=88=EB=8B=A4=20=E3=80=8C=EB=8D=A4=ED=94=84=20?= =?UTF-8?q?=EC=A0=81=EC=9E=AC=E3=80=8D=20=EC=A7=9D=20=EC=A4=84=20=E2=80=94?= =?UTF-8?q?=2010-12=20=E3=80=8C1.=20=EC=A0=81=EC=9E=AC=E3=80=8D=20=C2=B7?= =?UTF-8?q?=20=EA=B1=B0=EB=A6=AC=20=EB=AC=B4=EA=B4=80=20=C2=B7=20=EC=88=98?= =?UTF-8?q?=EB=9F=89=EC=9D=80=20=EC=9A=B4=EB=B0=98=EB=9F=89=20=EA=B7=B8?= =?UTF-8?q?=EB=8C=80=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _haul_rows 한 곳: dump_truck 내역 줄 뒤에 haul_equipment dump_loading 짝 줄(갈래·수량 같음) - 흙깎기 9-3-2(㎝ 20초·135°)에 싣기가 안 들어 있어 운반과 한 벌로 냄(브레인 판정) - B09 운반토량 검산(운반 ≤ 절취)에서 적재 줄은 뺌 — 같은 흙을 두 번 세지 않게 - 시험 기대값: 운반표 줄 4 → 6(적재 둘) · 내역 줄 3 → 5 · 전체 1763 통과 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- .../B08_Quantity_Engine_Handoff_Rows.py | 19 +++++++++++++++++++ .../B09_Estimation_BillOfQuantities.py | 10 +++++++++- resources/tester/test_b08_handoff.py | 13 ++++++++++--- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py index fd748b2b..c4ddcfb6 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py @@ -288,9 +288,28 @@ def _haul_rows( "origin": ORIGIN_HAUL, } ) + if equipment == "dump_truck" and in_bill: + # 덤프 운반은 **싣는 일이 따로** — 산림품셈 10-12 「1. 적재」 + # (굴착기 0.7㎥ · ㎝ 22초·180°). + # 흙깎기 9-3-2(㎝ 20초·135°, 깎아 옆에 둠)에 안 들어 있어 운반과 한 벌로 짝 줄을 냄 + # (2026-09-14 브레인 판정). 거리와 무관 · 수량은 운반량 그대로. + rows.append( + { + **rows[-1], + "name": "덤프 적재", + "haul_distance_m": None, + "haul_equipment": LOADING_EQUIPMENT, + "spec_detail": f"{state_note} · 덤프 운반량만큼 싣기(10-12 「1. 적재」)", + } + ) return rows, unmatched +#: 인계 「덤프 적재」 짝 줄의 수단 표지 — +#: B09 `MachineProductivity_Dump.LOADING_EQUIPMENT` 와 같은 글. +LOADING_EQUIPMENT = "dump_loading" + + def blocked_of( structure: dict[str, Any], class_basis: str = "", has_code: bool = False ) -> tuple[str | None, str]: diff --git a/B09_Estimation/B09_Estimation_BillOfQuantities.py b/B09_Estimation/B09_Estimation_BillOfQuantities.py index 6f67830c..86b1c528 100644 --- a/B09_Estimation/B09_Estimation_BillOfQuantities.py +++ b/B09_Estimation/B09_Estimation_BillOfQuantities.py @@ -628,7 +628,15 @@ def build_bill( ), _ZERO, ) - haul_total = sum((item.quantity for item in work_items if item.haul_equipment), _ZERO) + # 「덤프 적재」 짝 줄은 운반이 아니라 싣기 — 같은 흙을 운반량으로 두 번 세지 않게 뺌. + haul_total = sum( + ( + item.quantity + for item in work_items + if item.haul_equipment and item.haul_equipment != "dump_loading" + ), + _ZERO, + ) if cut_total > 0 and haul_total > 0: check_haul_volume_within_cut( haul_volume_total_m3=haul_total, diff --git a/resources/tester/test_b08_handoff.py b/resources/tester/test_b08_handoff.py index c31fc7e2..016d73dc 100644 --- a/resources/tester/test_b08_handoff.py +++ b/resources/tester/test_b08_handoff.py @@ -188,6 +188,11 @@ def test_무대는_넘기되_내역에는_안_섬() -> None: assert dump["in_bill"] is True assert dump["work_item_code"] == "FP-10-12" assert dump["haul_distance_m"] == pytest.approx(1200.0) + # 2026-09-14 — 덤프 운반마다 「덤프 적재」 짝 줄(10-12 「1. 적재」) · 거리 없음 · 수량 같음 + loading = 줄(handoff, "덤프 적재") + assert loading["haul_equipment"] == "dump_loading" and loading["haul_distance_m"] is None + assert loading["quantity"] == dump["quantity"] and loading["variant_value"] == "토사" + assert not any(r["name"] == "덤프 적재" and r["spec"] != "토사" for r in handoff["work_items"]) def test_합계_줄은_내역에_안_섬() -> None: @@ -600,9 +605,11 @@ def 운반표() -> dict: def test_운반계획이_있으면_네_줄이_실림() -> None: handoff = build_handoff(haul_table=운반표()) haul_rows = [row for row in handoff["work_items"] if row["origin"] == "haul"] - assert len(haul_rows) == 4 + # 운반 넷 + 덤프 운반 둘의 적재 짝 줄 둘(2026-09-14 · 10-12 「1. 적재」 — 거리 없음) + moves = [row for row in haul_rows if row["haul_equipment"] != "dump_loading"] + assert len(moves) == 4 and len(haul_rows) == 6 assert all(row["haul_equipment"] for row in haul_rows) - assert all(row["haul_distance_m"] for row in haul_rows) + assert all(row["haul_distance_m"] for row in moves) def test_무대가_함께_와야_운반_검산이_걸림() -> None: @@ -612,7 +619,7 @@ def test_무대가_함께_와야_운반_검산이_걸림() -> None: assert free["in_bill"] is False assert free["quantity"] == pytest.approx(871.0) assert handoff["excluded_row_count"] == 1 - assert handoff["bill_row_count"] == 3 + assert handoff["bill_row_count"] == 5 # 도자 · 덤프 운반 둘 · 덤프 적재 짝 줄 둘 def test_운반계획이_없으면_줄이_없음() -> None: From 9cd2920afcf8ca5beb27ce84a85a68001c21731a Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 09:01:37 +0900 Subject: [PATCH 3/4] =?UTF-8?q?knowledge(=EC=B6=95C=20=EB=AA=85=EC=84=B8):?= =?UTF-8?q?=20=EC=B4=9D=EC=95=A1=201,000=EC=9B=90=20=EB=AF=B8=EB=A7=8C?= =?UTF-8?q?=EB=B2=84=EB=A6=BC=20=EC=8B=A4=EC=B8=A1=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 고시 다섯 줄 중 총액 줄만 우리가 안 따르고 있었음(기본값 꺼짐) - 실무 6건 끝 세 자리 000 ↔ 우리 373·695·578·654·341·639 - 곁 버그 — 절사 자동보정이 둘째 걸음부터 ÷1.1 누락으로 진동 - 집계표 ROUND 는 설계서 소계와 다른 자리로 봄 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0154EMdwPYNPuKc9eZv8SLEX --- docs/raw/verification/2026-09-13_축C_명세.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/raw/verification/2026-09-13_축C_명세.md b/docs/raw/verification/2026-09-13_축C_명세.md index bcb21055..a9e4d7bd 100644 --- a/docs/raw/verification/2026-09-13_축C_명세.md +++ b/docs/raw/verification/2026-09-13_축C_명세.md @@ -358,7 +358,7 @@ B08 인계본 `ground_class_aliases` — **B09 가 안 읽음**)을 한 벌로 | 종목 | 단위 | 지위 | 비고 | 우리 실측과 | | --- | --- | --- | --- | --- | -| 설계서의 **총액** | 원 | **1,000** | 미만버림 | ⚠ **아직 확인 안 됨** — 데스크탑 서브가 재는 중 | +| 설계서의 **총액** | 원 | **1,000** | 미만버림 | ⚠ **우리는 안 버리고 있었음** — 실무 6건은 여섯 다 `000`, 우리는 `373·695·578·654·341·639`. 기본을 켜는 것으로 판정(2026-09-14) | | 설계서의 소계 | 원 | 1 | 미만버림 | ✅ | | 설계서의 **금액란** | 원 | 1 | 미만버림 | ✅ 「내역에 실릴 때 정수 절사」와 같음 | | **일위대가표의 계금** | 원 | 1 | 미만버림 | ✅ 「호표 안 성분 소계 절사」와 같음 | @@ -366,6 +366,15 @@ B08 인계본 `ground_class_aliases` — **B09 가 안 읽음**)을 한 벌로 ⇒ **STmate 를 재서 얻은 규칙이 법 조문이었음.** 경쟁사를 베낀 것이 아니라 둘 다 같은 고시를 따른 것임. +⚠ **총액 줄만 우리가 안 따르고 있었음** (2026-09-14 데스크탑 서브 실측). 기본값이 꺼져 있어(`cut_basis=""`) +실무 6건의 총공사비 끝 세 자리가 우리 엔진에서만 남음. **기본을 켜되 화면에 근거를 보일 것** — +2026-09-08 「조용히 깎지 않음」 합의는 근거가 없을 때 정한 것이고, 지금은 고시가 근거임. +곁에 버그 하나 — 절사 자동보정이 둘째 걸음부터 ÷1.1 을 안 해 경계를 지나쳐 **진동**함 +(울진 신설 `654→999→900→910→909…`). 다섯 건은 첫 걸음에 앉아 안 드러났음. + +⚠ 「설계서의 소계 1원 미만버림」과 우리 「집계표 행 금액 `ROUND`(279/279)」는 **다른 자리**로 봄 — +집계표는 설계서 소계가 아님. 같은 자리로 읽을 근거가 나오면 다시 잴 것. + **같은 고시 1-2-2 「1. 수량의 단위표준」 [주]**: ``` From d5bdd2ee17c401122427a53689b0b171c08d0f37 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 09:02:11 +0900 Subject: [PATCH 4/4] =?UTF-8?q?docs(b09):=20=EC=B6=95C=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=EC=97=90=20=ED=92=88=EC=85=88=20=ED=8C=90=20=EA=B7=BC?= =?UTF-8?q?=EA=B1=B0=EC=99=80=20=EA=B8=88=EC=95=A1=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=ED=91=9C=EC=A4=80=20=EC=A0=88=20=EB=B3=B4=ED=83=AC=20=E2=80=94?= =?UTF-8?q?=20=EC=82=B0=EB=A6=BC=EC=B2=AD=EA=B3=A0=EC=8B=9C=202025-82?= =?UTF-8?q?=ED=98=B8?= 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_01Prk9BCHG1EMAywk9k8wegA --- docs/raw/verification/2026-09-13_축C_명세.md | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/raw/verification/2026-09-13_축C_명세.md b/docs/raw/verification/2026-09-13_축C_명세.md index 2b3a1862..a9e4d7bd 100644 --- a/docs/raw/verification/2026-09-13_축C_명세.md +++ b/docs/raw/verification/2026-09-13_축C_명세.md @@ -90,6 +90,24 @@ FP-<절>-<절>-<절> 예) FP-04-02-02 (벌목, 5m 미만) - 정본은 **품셈 절 번호**. 표의 행은 그 절의 **조건별 변형**(`variant` 칸)임 - **이름은 표시용, 잇기는 코드로.** 이름으로 맞추는 코드가 남아 있으면 그것이 곧 결함임 + +### ⭐ 어느 품셈 판인가 — 2026-09-14 확인 (조문 근거 있음) + +**`FP-` 는 산림사업 표준품셈 한 판임.** 건설공사 표준품셈은 **공종 축이 아니라 기계 층에만** 들어옴. + +``` +공종 축 477/477 FP- · 원천 pum_forest_2026.json (산림청고시 2025-82호, 2026-01-01) +기계 층 pum_const_2026.json · mach_base_2026.json (건설품셈 제8장 — 취득가·손료계수·연료·운전원) +빌려 온 몇 곳 「교차 참조」 딱지를 붙임 (예: 뿌리뽑기 밑수 3-9-2) +``` + +**근거 — 같은 고시 총칙 7**: 「본 품셈에 명시되지 않은 품은 타 부문(토목, 건축, 전기, 기계 등)의 +표준품셈에 명시된 부분의 품을 적용하고, **타 부문과 유사한 공종의 품은 본 품셈을 우선하여 적용**한다.」 + +⇒ 지금 얼개가 이 조문 그대로임. **건설품셈 절번호로 선 공종 줄은 0 건**(소스에 「8-2-8」 참조 0건). +⚠ 자료를 찾을 때 건설품셈만 보면 「값이 없다」고 잘못 결론 내기 쉬움 — 덤프 운반이 그랬음 +(건설 8-2-8 에는 단위중량·적재용량 표가 없으나 **산림 10-12 [주]에 토사 1.9 · 암 2.4 ton/㎥ · 덤프 15 ton 이 있었음**). +**막히면 산림품셈 쪽을 먼저 볼 것.** - 부모·잎이 있음. ⚠ **「부모에 수량 금지」는 반쪽만 맞았음** — 2026-09-13 정정. **부모가 두 모양이고 규칙이 다름**(랩탑 메인 실측). @@ -334,6 +352,40 @@ B08 인계본 `ground_class_aliases` — **B09 가 안 읽음**)을 한 벌로 | **단가산출 성분단가** | 소수 1자리 절사 | 211건 중 137건 설명 | | 내부 계산 | 은행가(최근접-짝수) | STmate 내부. ⚠ 뽑은 엑셀은 사사오입이라 20개 경계 중 10개가 갈림 | +### ⭐⭐ 2026-09-14 — 위 실측이 **고시 조문과 그대로 맞았음** + +**산림청고시 제2025-82호 「산림사업 표준품셈」 1-2-2 「2. 금액의 단위표준」**: + +| 종목 | 단위 | 지위 | 비고 | 우리 실측과 | +| --- | --- | --- | --- | --- | +| 설계서의 **총액** | 원 | **1,000** | 미만버림 | ⚠ **우리는 안 버리고 있었음** — 실무 6건은 여섯 다 `000`, 우리는 `373·695·578·654·341·639`. 기본을 켜는 것으로 판정(2026-09-14) | +| 설계서의 소계 | 원 | 1 | 미만버림 | ✅ | +| 설계서의 **금액란** | 원 | 1 | 미만버림 | ✅ 「내역에 실릴 때 정수 절사」와 같음 | +| **일위대가표의 계금** | 원 | 1 | 미만버림 | ✅ 「호표 안 성분 소계 절사」와 같음 | +| **일위대가표의 금액란** | 원 | **0.1** | 미만버림 | ✅ 「단가산출 성분단가 소수 1자리 절사」와 같음 | + +⇒ **STmate 를 재서 얻은 규칙이 법 조문이었음.** 경쟁사를 베낀 것이 아니라 둘 다 같은 고시를 따른 것임. + +⚠ **총액 줄만 우리가 안 따르고 있었음** (2026-09-14 데스크탑 서브 실측). 기본값이 꺼져 있어(`cut_basis=""`) +실무 6건의 총공사비 끝 세 자리가 우리 엔진에서만 남음. **기본을 켜되 화면에 근거를 보일 것** — +2026-09-08 「조용히 깎지 않음」 합의는 근거가 없을 때 정한 것이고, 지금은 고시가 근거임. +곁에 버그 하나 — 절사 자동보정이 둘째 걸음부터 ÷1.1 을 안 해 경계를 지나쳐 **진동**함 +(울진 신설 `654→999→900→910→909…`). 다섯 건은 첫 걸음에 앉아 안 드러났음. + +⚠ 「설계서의 소계 1원 미만버림」과 우리 「집계표 행 금액 `ROUND`(279/279)」는 **다른 자리**로 봄 — +집계표는 설계서 소계가 아님. 같은 자리로 읽을 근거가 나오면 다시 잴 것. + +**같은 고시 1-2-2 「1. 수량의 단위표준」 [주]**: + +``` +① 설계서 수량의 단위와 소수자리 표시는 본 표에 따르며, 반올림하여 적용 +② 품목이 제시한 소숫자리가 본 표와 다르면 항목 쪽을 우선 +③ 본 표에 없는 품은 유사품의 규격·단위수량을 참고, C.G.S 단위 원칙 +``` + +⇒ 아래 「규정이 없는 자리 = 소수 2자리」는 **이 표에도 없는 자리에만** 적용할 것. +표에 실린 품목(관류 길이 m 소수 2 · 수로연장 m 소수 1 · 옹벽 ㎡ 소수 1 …)은 **표가 규정임.** + ### ⭐ 규정이 없는 자리 — 소수점 2자리가 기본값 (2026-09-13 사용자 확정) **단수처리에 법적 근거가 없음이 확인됐음** — 기재부 **예정가격작성기준 전 조문에