From 75ae9375d993d14b092f64a11fce3ed38978cbfc Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 16 Sep 2026 13:08:52 +0900 Subject: [PATCH] =?UTF-8?q?fix(z01):=20=EC=9E=90=EC=9E=AC=20=ED=95=A0?= =?UTF-8?q?=EC=A6=9D=20=EB=91=90=20=EB=B2=8C=20=EA=B4=80=EA=B3=84=20?= =?UTF-8?q?=EB=AA=85=EA=B8=B0=20=C2=B7=20coef=20=EC=95=94=EA=B4=B4=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=ED=95=9C=20=EC=A4=84=EB=A1=9C=20=C2=B7=20?= =?UTF-8?q?=EC=B0=B8=EA=B3=A0=20=EC=82=AC=EB=A1=80=20=EA=B0=88=EB=9E=98=20?= =?UTF-8?q?=EB=91=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - **이원화가 아님을 두 자리에 못박음** — `material_surcharge.original_tables` 「원문 표는 coef/surcharge_mat」 · `coef.variables.surcharge_mat.used_value_at` 「읽어 쓰는 값은 material_surcharge/rates_pct」. 저쪽은 원문 보관이라 읽는 코드가 0 - **암괴 중복 두 줄** — L·C 둘 다 첫 줄에 `duplicate_same_value`, 둘째 줄에 `hidden_duplicate` + 사유 「원문 두 줄 · 값 같음 — 화면에는 한 줄로 보임. 원문은 그대로 둠」. ⚠ 원본 줄은 안 지움 · `selection: pending` 과 「원문 오기 가능성」 주석 그대로 살림 ⚠ 「역이 섞인 점질토」는 값이 달라 안 접음(L 1.35~1.4 ↔ 1.3~1.4) - **참고 사례 갈래(`reference`) 새로 둠** — `structure_unit_observed`(울진소광 한 현장 관찰값) · `revetment_sabang`(교본값에 설명 글 섞임). 트리엔 남되 기초데이터가 아님(사용자 판정). 갈래 셈 로직 17 → 15 · 참고 사례 2 - 마스터 둘이 바뀌어 폴더 지문(`_manifest`) 둘 갱신 · 새 열 넷·값 묶음 둘 이름표 추가 - ⚠ `test_z01_master_data.py`(랩탑 메인)가 갈래 수를 박아 두어 함께 고침 — 데이터가 바뀌면 따라가야 하는 상수임 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FFCnEYNH4tsS2MbHvzBhZk --- .../data_cost_input_value/_manifest.json | 4 +- .../data_cost_input_value/coef_2026.json | 11 +++ .../data_master_labels/labels_2026-01-01.json | 97 ++++++++++++++++--- .../data_material_surcharge/_manifest.json | 4 +- .../material_surcharge_2026-01-01.json | 1 + resources/tester/test_master_labels_cover.py | 4 +- resources/tester/test_z01_master_data.py | 10 +- 7 files changed, 114 insertions(+), 17 deletions(-) diff --git a/resources/data_cost_input_value/_manifest.json b/resources/data_cost_input_value/_manifest.json index 56945ecf..389c2456 100644 --- a/resources/data_cost_input_value/_manifest.json +++ b/resources/data_cost_input_value/_manifest.json @@ -13,8 +13,8 @@ "file": "coef_2026.json", "dataset_id": "coef", "effective_date": "2026-01-01", - "sha256": "0bc3fe981165e8d320b7c20f4535c545f2f54415427ac60205dac4a72dbb4f19", - "size_bytes": 29413 + "sha256": "daba170cd806374c92e086dec69f9559271c0317a7426e66e9dc841c2ca264bd", + "size_bytes": 31715 }, { "file": "fx_2026-08-14.json", diff --git a/resources/data_cost_input_value/coef_2026.json b/resources/data_cost_input_value/coef_2026.json index b2654f01..51cb6ef4 100644 --- a/resources/data_cost_input_value/coef_2026.json +++ b/resources/data_cost_input_value/coef_2026.json @@ -787,6 +787,7 @@ ] }, "surcharge_mat": { + "used_value_at": "resources/data_material_surcharge/material_surcharge_2026-01-01.json → rates_pct — ⚠ 코드가 읽어 쓰는 값은 저기다. 이 표는 원문 보관용이라 읽는 코드가 없다(두 벌이 아니다).", "unit": "percent", "representation": "full_source_tables", "duplicate_application_prohibited": true, @@ -1108,6 +1109,8 @@ "max": 1.45, "duplicate_group": "암괴나 호박돌이 섞인 점토", "duplicate_index": 1, + "duplicate_same_value": true, + "duplicate_reason": "원문 두 줄 · 값 같음 — 화면에는 한 줄로 보임. 원문은 그대로 둠(임의 보정 없음).", "selection": "pending" }, { @@ -1129,6 +1132,9 @@ "max": 1.45, "duplicate_group": "암괴나 호박돌이 섞인 점토", "duplicate_index": 2, + "duplicate_same_value": true, + "hidden_duplicate": true, + "duplicate_reason": "원문 두 줄 · 값 같음 — 화면에는 한 줄로 보임. 원문은 그대로 둠(임의 보정 없음).", "selection": "pending" } ] @@ -1210,6 +1216,8 @@ "max": 0.95, "duplicate_group": "암괴나 호박돌이 섞인 점토", "duplicate_index": 1, + "duplicate_same_value": true, + "duplicate_reason": "원문 두 줄 · 값 같음 — 화면에는 한 줄로 보임. 원문은 그대로 둠(임의 보정 없음).", "selection": "pending" }, { @@ -1231,6 +1239,9 @@ "max": 0.95, "duplicate_group": "암괴나 호박돌이 섞인 점토", "duplicate_index": 2, + "duplicate_same_value": true, + "hidden_duplicate": true, + "duplicate_reason": "원문 두 줄 · 값 같음 — 화면에는 한 줄로 보임. 원문은 그대로 둠(임의 보정 없음).", "selection": "pending" } ] diff --git a/resources/data_master_labels/labels_2026-01-01.json b/resources/data_master_labels/labels_2026-01-01.json index 4607da7e..d43c8ff1 100644 --- a/resources/data_master_labels/labels_2026-01-01.json +++ b/resources/data_master_labels/labels_2026-01-01.json @@ -2,14 +2,14 @@ "schema_version": "1.0", "dataset_id": "data_master_labels", "effective_date": "2026-01-01", - "generated_at": "2026-09-16T11:39:55+09:00", + "generated_at": "2026-09-16T13:00:09+09:00", "note": "마스터 자료의 **사람이 읽을 이름표**. 값이 아니라 이름만 담는다 — 여기를 고쳐도 계산은 안 바뀐다. Z01 마스터 관리 화면이 표·열 이름을 여기서 읽는다.", "policy": { "labels_only": "값·수식·단가를 담지 않는다. 마스터 파일은 손대지 않는다.", "no_invented_meaning": "뜻을 모르는 열은 name_ko 를 비우고 unknown 에 사유를 적는다. 지어내지 않는다.", "unit_is_label_only": "unit 은 이름표에서만 정한다. 자료에 없는 단위를 만들어 붙이지 않는다.", "counting_rule": "표 = 화면이 표로 그릴 마디(줄이 여럿인 마디). 값 묶음 = 표가 아닌 마디(설명·방침·한 값짜리 요율 따위).", - "brain_kind_table": "갈래는 2026-09-15 브레인 갈래 나눔표를 그대로 따른다(로직 17 · 기초값 9 · 부산물 4 · 씨앗 1).", + "brain_kind_table": "갈래는 2026-09-15 브레인 갈래 나눔표에서 출발한다(로직 17 · 기초값 9 · 부산물 4 · 씨앗 1). 2026-09-16 사용자 판정으로 `structure_unit_observed`·`revetment_sabang` 둘을 **참고 사례**로 옮겼다 — 한 현장 관찰값·교본 설명 섞임이라 기초데이터가 아니다(로직 15 · 참고 사례 2).", "out_of_31": "폴더 지문(_manifest.json) 셋은 브레인 31 밖이다. 부산물이 아니라 **지문** 갈래로 둔다 — 계산이 남긴 기록이 아니라 어느 판으로 셈했는지 못박는 표라 「정본 아님」 딱지가 안 맞는다(2026-09-15 브레인 ②).", "excluded": "강우 IDF 캐시(resources/data_rainfall_idf_cache, 96 파일)는 배수용이라 뺀다.", "merged_tables_rule": "`merged_tables` 는 **기초단가 다섯**(노임·자재·유가·제비율·기계)을 화면이 한 표씩으로 보이려고 여러 원본 표를 모은 자리다. ⚠ **값을 옮기지 않는다** — 열 이름·단위·숨김과 `editable`(고칠 수 있나)만 담는다. `editable: false` 는 다른 값에서 나온 계산값이라 사람이 못 고친다. 기계는 한 표로 안 모이므로 `machine_cost_chain` 이 사슬을 적는다 (2026-09-15 사용자 지시 · 브레인).", @@ -45,6 +45,10 @@ "name_ko": "지문", "summary": "어느 판으로 셈했는지 못박는 자리. 값도 규칙도 아니고 **판을 가리키는 표**다 — 고치지 않는다." }, + "reference": { + "name_ko": "참고 사례", + "summary": "한 현장·한 교본에서 본 값. ⚠ 표준이 아니다 — 근거로 보되 기초데이터처럼 갈아 끼우지 않는다." + }, "seed": { "name_ko": "씨앗", "summary": "앞으로 늘려 갈 첫 벌. 모양을 잡아 두려고 먼저 놓은 것이다." @@ -1090,11 +1094,11 @@ "merged_columns": 89, "files": 34, "tables": 91, - "columns": 456, - "value_groups": 173, + "columns": 462, + "value_groups": 175, "unknown": 0, "value_groups_by_kind": { - "doc": 107, + "doc": 109, "value": 66 } }, @@ -1484,6 +1488,24 @@ "name_ko": "채택 여부", "unit": "", "visible": true + }, + { + "key": "duplicate_same_value", + "name_ko": "값 같은 중복", + "unit": "", + "visible": true + }, + { + "key": "duplicate_reason", + "name_ko": "중복 사유", + "unit": "", + "visible": true + }, + { + "key": "hidden_duplicate", + "name_ko": "접힌 줄", + "unit": "", + "visible": false } ] }, @@ -1529,6 +1551,24 @@ "name_ko": "채택 여부", "unit": "", "visible": true + }, + { + "key": "duplicate_same_value", + "name_ko": "값 같은 중복", + "unit": "", + "visible": true + }, + { + "key": "duplicate_reason", + "name_ko": "중복 사유", + "unit": "", + "visible": true + }, + { + "key": "hidden_duplicate", + "name_ko": "접힌 줄", + "unit": "", + "visible": false } ] }, @@ -1665,6 +1705,12 @@ "name_ko": "값 단위", "summary": "할증은 %다.", "kind": "value" + }, + { + "key": "variables/surcharge_mat/used_value_at", + "name_ko": "쓰는 값이 있는 자리", + "summary": "이 표는 원문 보관용이라 읽는 코드가 없다. 값은 `material_surcharge` 의 `rates_pct` 를 읽는다.", + "kind": "doc" } ] }, @@ -1827,6 +1873,12 @@ "summary": "실무 집계에서 본 할증률. 참고이지 기본값이 아니다.", "kind": "value" }, + { + "key": "original_tables", + "name_ko": "원문 표가 실린 자리", + "summary": "같은 값이 `coef` 의 `surcharge_mat` 에도 있다. ⚠ 두 벌이 아니다 — 저쪽은 원문 보관이고 읽어 쓰는 정본은 여기다.", + "kind": "doc" + }, { "key": "policy", "name_ko": "방침", @@ -2562,9 +2614,9 @@ { "file_id": "revetment_sabang", "path": "resources/data_masonry/revetment_sabang_2026-01-01.json", - "name_ko": "기슭막이 치수(사방)", - "summary": "독립 기슭막이의 높이·계획비탈·둑마루 두께·뒷채움을 한 자리에 둔 벌.", - "kind": "logic", + "name_ko": "기슭막이 치수(사방) — 참고 사례", + "summary": "사방기술교본 값에 설명 글이 섞인 벌. ⚠ 기초데이터가 아니다 — 표준값처럼 쓰지 말고 근거로만 본다(2026-09-16 사용자 판정).", + "kind": "reference", "tables": [], "value_groups": [ { @@ -2596,9 +2648,9 @@ { "file_id": "structure_unit_observed", "path": "resources/data_structure_unit/structure_unit_observed_2026-01-01.json", - "name_ko": "구조물 관측 원단위", - "summary": "품셈에 표준 물량표가 없는 콘크리트 구조물을 실무 설계원본에서 뽑은 벌.", - "kind": "logic", + "name_ko": "구조물 관측 원단위 — 참고 사례", + "summary": "울진소광 **한 현장**을 관찰한 값이다. ⚠ 기초데이터가 아니다 — 규격이 다르면 안 맞고 표준도 아니다(2026-09-16 사용자 판정).", + "kind": "reference", "tables": [ { "key": "double_count_rules/rules", @@ -6320,6 +6372,16 @@ "unit": "", "visible": true }, + "duplicate_reason": { + "name_ko": "중복 사유", + "unit": "", + "visible": true + }, + "duplicate_same_value": { + "name_ko": "값 같은 중복", + "unit": "", + "visible": true + }, "duration_bracket": { "name_ko": "공사기간 구간", "unit": "", @@ -6470,6 +6532,11 @@ "unit": "", "visible": true }, + "hidden_duplicate": { + "name_ko": "접힌 줄", + "unit": "", + "visible": false + }, "hours_per_day": { "name_ko": "1일 근로시간", "unit": "시간", @@ -7479,6 +7546,10 @@ "name_ko": "값 단위", "summary": "할증은 %다." }, + "coef::variables/surcharge_mat/used_value_at": { + "name_ko": "쓰는 값이 있는 자리", + "summary": "이 표는 원문 보관용이라 읽는 코드가 없다. 값은 `material_surcharge` 의 `rates_pct` 를 읽는다." + }, "formwork_reuse::reuse_ratio_pct": { "name_ko": "사용횟수별 기준수량 비율", "summary": "품셈 12-4 — 일위대가 재료비에 걸린다. B08 이 곱하면 이중계상이다." @@ -7599,6 +7670,10 @@ "name_ko": "실무 관측", "summary": "실무 집계에서 본 할증률. 참고이지 기본값이 아니다." }, + "material_surcharge::original_tables": { + "name_ko": "원문 표가 실린 자리", + "summary": "같은 값이 `coef` 의 `surcharge_mat` 에도 있다. ⚠ 두 벌이 아니다 — 저쪽은 원문 보관이고 읽어 쓰는 정본은 여기다." + }, "oil::variables/oil_diesel": { "name_ko": "경유 단가", "summary": "전국평균 경유 값(원/L)." diff --git a/resources/data_material_surcharge/_manifest.json b/resources/data_material_surcharge/_manifest.json index 31b2bde7..a8192720 100644 --- a/resources/data_material_surcharge/_manifest.json +++ b/resources/data_material_surcharge/_manifest.json @@ -6,8 +6,8 @@ "files": [ { "file": "material_surcharge_2026-01-01.json", - "sha256": "30a1e11acb6f6bd52c72e0ff471c96f772917858306ea61f66ecbcb5df433c9f", - "size_bytes": 5087 + "sha256": "1394a39be22ac0eb7476e4bc2b81c1e23c365a4ad5b915befe85d3f0f99023d5", + "size_bytes": 5344 } ] } diff --git a/resources/data_material_surcharge/material_surcharge_2026-01-01.json b/resources/data_material_surcharge/material_surcharge_2026-01-01.json index 2e732b1e..03a971a1 100644 --- a/resources/data_material_surcharge/material_surcharge_2026-01-01.json +++ b/resources/data_material_surcharge/material_surcharge_2026-01-01.json @@ -1,6 +1,7 @@ { "schema_version": "1.0", "dataset_id": "material_surcharge", + "original_tables": "resources/data_cost_input_value/coef_2026.json → variables/surcharge_mat — 원문 표가 실린 자리. ⚠ 값이 겹치지만 이원화가 아니다: 저쪽은 원문 보관이고 읽어 쓰는 정본은 여기(rates_pct)다.", "effective_date": "2026-01-01", "source": { "primary": { diff --git a/resources/tester/test_master_labels_cover.py b/resources/tester/test_master_labels_cover.py index 8e5504f9..8f08bcd7 100644 --- a/resources/tester/test_master_labels_cover.py +++ b/resources/tester/test_master_labels_cover.py @@ -19,7 +19,9 @@ ROOT = Path(__file__).resolve().parents[2] LABELS_PATH = ROOT / "resources" / "data_master_labels" / "labels_2026-01-01.json" #: 브레인 갈래 나눔표(2026-09-15). 폴더 장부(_manifest)는 그 31 밖이라 따로 센다. -BRAIN_KIND_COUNTS = {"logic": 17, "base_value": 9, "byproduct": 4, "seed": 1} +#: 2026-09-16 사용자 판정 — `structure_unit_observed`(울진소광 한 현장 관찰값)와 +#: `revetment_sabang`(교본값에 설명 글 섞임)은 기초데이터가 아니라 **참고 사례**다. +BRAIN_KIND_COUNTS = {"logic": 15, "base_value": 9, "byproduct": 4, "seed": 1, "reference": 2} MANIFEST_PREFIX = "manifest_" META_KEYS = { diff --git a/resources/tester/test_z01_master_data.py b/resources/tester/test_z01_master_data.py index f7f00f68..3814a58e 100644 --- a/resources/tester/test_z01_master_data.py +++ b/resources/tester/test_z01_master_data.py @@ -49,7 +49,15 @@ def test_갈래는_이름표_차례_파일_34_강우_캐시는_없음(client: Te groups = _tree(client)["groups"] assert [g["key"] for g in groups] == [k for k in LABELS["kinds"]] counts = {g["key"]: len(g["files"]) for g in groups} - assert counts == {"logic": 17, "base_value": 9, "byproduct": 4, "fingerprint": 3, "seed": 1} + # 2026-09-16 사용자 판정 — 한 현장 관찰값·교본 설명 섞임 둘은 참고 사례로 옮겼다. + assert counts == { + "logic": 15, + "base_value": 9, + "byproduct": 4, + "reference": 2, + "fingerprint": 3, + "seed": 1, + } assert {g["key"]: g["label"] for g in groups}["base_value"] == "기초값" by_id = {f["file_id"]: f for f in LABELS["files"]} for f in (f for g in groups for f in g["files"]):