feat(z01): 이름표 전수 — 공종 축 산출 일곱을 올리고 영문으로 뜨던 칸 열넷에 이름

평평한 목록 열(산림 8·건설 8)은 이미 다 한글이었으나, **파일 보기 쪽에 구멍**이 있었음 —
공종 축 산출 일곱이 이름표 `files` 에 아예 없어 덮개 시험도 그 파일을 안 훑고 있었음.

- 올린 일곱 — 건설 공종 마스터 · 건설 형태 미판정 · 건설 밑수 미확보(byproduct) ·
  열쇠 장부 둘 · 구실 잣대(logic) · 건설 폴더 지문(fingerprint)
- 영문으로 뜨던 칸 열넷에 이름 — `general_provision_roots`「총칙 뿌리」 ·
  `code_shape_why`「코드 꼴 까닭」 · `settled`「확정됐나」 · `relinked`「손으로 이은 자리」 따위
- 사전 모양 표 넷에 줄 이름 열 — 「산림 공종 열쇠」·「건설 공종 열쇠」·「구실 이름」·「갈래 이름」
- 잣대 칸 이름 통일 — `counts_2025_82`·`counts_2026` → `counts`(판이 바뀌어도 안 갈림)
- 다시 안 생기게 시험 하나 — `test_이름이_영문으로_떨어진_칸이_없다`
  (「비지 않았나」만 보던 옛 시험은 영문 키가 이름 자리에 앉은 것을 못 잡았음)
- 못박은 수 갱신 — 파일 34→41 · logic 15→19 · byproduct 4→6 · 지문 3→4 · 표 92→100

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsGw1Dz9HmhuAxGisxmDPF
This commit is contained in:
2026-09-17 16:29:20 +09:00
co-authored by Claude Opus 5
parent 43d7644382
commit f5860e6f12
4 changed files with 720 additions and 16 deletions
@@ -1931,13 +1931,13 @@
"counts": {
"merged_tables": 17,
"merged_columns": 172,
"files": 34,
"tables": 92,
"columns": 471,
"value_groups": 177,
"files": 41,
"tables": 100,
"columns": 532,
"value_groups": 202,
"unknown": 0,
"value_groups_by_kind": {
"doc": 111,
"doc": 136,
"value": 66
}
},
@@ -7038,6 +7038,675 @@
"kind": "doc"
}
]
},
{
"file_id": "work_item_master_const",
"path": "resources/data_work_item_master/const_work_item_master_2026-01-01.json",
"name_ko": "건설 공종 마스터",
"summary": "건설공사 표준품셈 목차를 나무로 세운 벌. 줄의 열쇠는 `work_item_key`(CW-…)이고 목차 코드(CP-…)·번호·판은 칸이다.",
"kind": "logic",
"tables": [
{
"key": "work_items",
"name_ko": "공종",
"summary": "건설 공종 나무. 부문 다섯이 뿌리이고, 부문마다 장 번호가 1부터 다시 시작한다.",
"shape": "list",
"rows": 1367,
"columns": [
{
"key": "work_item_code",
"name_ko": "공종코드",
"unit": "",
"visible": true
},
{
"key": "number",
"name_ko": "품셈 번호",
"unit": "",
"visible": true
},
{
"key": "name",
"name_ko": "이름",
"unit": "",
"visible": true
},
{
"key": "division",
"name_ko": "부문",
"unit": "",
"visible": true
},
{
"key": "level",
"name_ko": "단계",
"unit": "",
"visible": true
},
{
"key": "parent_code",
"name_ko": "상위 공종코드",
"unit": "",
"visible": true
},
{
"key": "sort_order",
"name_ko": "정렬 차례",
"unit": "",
"visible": false
},
{
"key": "tables",
"name_ko": "딸린 품셈 표",
"unit": "",
"visible": true
},
{
"key": "parent_mode",
"name_ko": "하위 고르는 방식",
"unit": "",
"visible": true
},
{
"key": "variant_keys",
"name_ko": "갈래 키",
"unit": "",
"visible": true
},
{
"key": "work_item_key",
"name_ko": "공종 열쇠",
"unit": "",
"visible": true
},
{
"key": "toc_code",
"name_ko": "그 판 목차 코드",
"unit": "",
"visible": true
},
{
"key": "toc_number",
"name_ko": "그 판 목차 번호",
"unit": "",
"visible": true
},
{
"key": "toc_edition",
"name_ko": "목차 판(고시)",
"unit": "",
"visible": false
},
{
"key": "path_name",
"name_ko": "전체 경로 이름",
"unit": "",
"visible": true
},
{
"key": "axis_role",
"name_ko": "줄 구실",
"unit": "",
"visible": true
}
]
}
],
"value_groups": [
{
"key": "toc_edition",
"name_ko": "목차 판(고시)",
"summary": "목차 번호가 묶인 고시 판. 값이 아니라 **판을 가리키는 표**다.",
"kind": "doc"
},
{
"key": "policy",
"name_ko": "방침",
"summary": "이 벌을 다룰 때 지킬 것 — 지어내지 않기·빈칸 안 두기 같은 것.",
"kind": "doc"
},
{
"key": "stats",
"name_ko": "집계",
"summary": "몇 줄이 섰고 몇 줄이 빠졌는지 센 것.",
"kind": "doc"
},
{
"key": "toc_corrections",
"name_ko": "목차 오기 바로잡음",
"summary": "목차 번호가 본문과 다른 자리를 본문 번호로 고친 기록.",
"kind": "doc"
},
{
"key": "toc_duplicate_rows",
"name_ko": "목차 번호가 겹친 줄",
"summary": "목차가 같은 번호를 두 번 적었는데 본문 번호로도 못 가른 자리. 비어 있어야 정상 — 줄이 생기면 표가 딴 줄에 붙는다.",
"kind": "doc"
},
{
"key": "orphan_tables",
"name_ko": "공종에 못 붙인 표",
"summary": "목차 어디에도 못 붙인 품셈 표.",
"kind": "doc"
}
]
},
{
"file_id": "const_form_undetermined",
"path": "resources/data_work_item_master/const_form_undetermined_2026-01-01.json",
"name_ko": "건설 형태 미판정 표",
"summary": "생산량형인지 소요량형인지 못 정한 건설 표. 사람이 보고 확정할 자리다.",
"kind": "byproduct",
"tables": [
{
"key": "items",
"name_ko": "형태 미판정 표",
"summary": "생산량형인지 소요량형인지 못 정한 표.",
"shape": "list",
"rows": 881,
"columns": [
{
"key": "pum_table_id",
"name_ko": "품셈 표 번호",
"unit": "",
"visible": true
},
{
"key": "section",
"name_ko": "품셈 절",
"unit": "",
"visible": true
},
{
"key": "headers",
"name_ko": "표 머리",
"unit": "",
"visible": true
},
{
"key": "first_rows",
"name_ko": "첫 줄들",
"unit": "",
"visible": true
},
{
"key": "reason",
"name_ko": "사유",
"unit": "",
"visible": true
}
]
}
],
"value_groups": [
{
"key": "note",
"name_ko": "설명",
"summary": "이 벌이 무엇이고 어떻게 쓰는지 적은 글.",
"kind": "doc"
}
]
},
{
"file_id": "const_basis_missing",
"path": "resources/data_work_item_master/const_basis_missing_2026-01-01.json",
"name_ko": "건설 밑수 미확보 표",
"summary": "「10㎡당」 같은 기준 수량을 못 찾은 건설 표. ⚠ 1 단위당으로 단정하면 곱셈이 10배·100배 틀린다.",
"kind": "byproduct",
"tables": [
{
"key": "items",
"name_ko": "밑수 미확보 표",
"summary": "기준 수량을 못 찾은 표.",
"shape": "list",
"rows": 447,
"columns": [
{
"key": "pum_table_id",
"name_ko": "품셈 표 번호",
"unit": "",
"visible": true
},
{
"key": "section",
"name_ko": "품셈 절",
"unit": "",
"visible": true
},
{
"key": "pum_form",
"name_ko": "품셈 표 형태",
"unit": "",
"visible": true
},
{
"key": "line",
"name_ko": "원문 줄 번호",
"unit": "",
"visible": false
}
]
}
],
"value_groups": [
{
"key": "note",
"name_ko": "설명",
"summary": "이 벌이 무엇이고 어떻게 쓰는지 적은 글.",
"kind": "doc"
}
]
},
{
"file_id": "work_item_keys",
"path": "resources/data_work_item_master/work_item_keys.json",
"name_ko": "산림 공종 열쇠 장부",
"summary": "산림 공종의 불변 열쇠(FW-…) 장부. 판이 바뀌어도 이 열쇠는 그 공종 것이다.",
"kind": "logic",
"tables": [
{
"key": "keys",
"name_ko": "열쇠 발급 내역",
"summary": "열쇠마다 처음 낸 판·날짜·목차 코드·이름.",
"shape": "map",
"rows": 477,
"columns": [
{
"key": "@key",
"name_ko": "산림 공종 열쇠",
"unit": "",
"visible": true,
"is_row_key": true
},
{
"key": "issued_edition",
"name_ko": "처음 낸 판",
"unit": "",
"visible": true
},
{
"key": "issued_on",
"name_ko": "처음 낸 날",
"unit": "",
"visible": true
},
{
"key": "first_toc_code",
"name_ko": "처음 목차 코드",
"unit": "",
"visible": true
},
{
"key": "first_toc_number",
"name_ko": "처음 목차 번호",
"unit": "",
"visible": true
},
{
"key": "first_name",
"name_ko": "처음 이름",
"unit": "",
"visible": true
},
{
"key": "relinked",
"name_ko": "손으로 이은 자리",
"unit": "",
"visible": true
}
]
}
],
"value_groups": [
{
"key": "note",
"name_ko": "메모",
"summary": "이 벌이 무엇인지 적어 둔 글.",
"kind": "doc"
},
{
"key": "policy",
"name_ko": "방침",
"summary": "이 벌을 다룰 때 지킬 것.",
"kind": "doc"
},
{
"key": "key_prefix",
"name_ko": "열쇠 앞꼬리",
"summary": "산림 FW · 건설 CW.",
"kind": "doc"
},
{
"key": "key_digits",
"name_ko": "열쇠 자릿수",
"summary": "다섯 자리.",
"kind": "doc"
},
{
"key": "next_serial",
"name_ko": "다음 일련번호",
"summary": "다음에 낼 열쇠 번호.",
"kind": "doc"
},
{
"key": "editions",
"name_ko": "판별 자리→열쇠",
"summary": "고시 판마다 목차 자리와 열쇠를 이은 사전. 낱값이 아니라 이음표다.",
"kind": "doc"
}
]
},
{
"file_id": "work_item_keys_const",
"path": "resources/data_work_item_master/work_item_keys_const.json",
"name_ko": "건설 공종 열쇠 장부",
"summary": "건설 공종의 불변 열쇠(CW-…) 장부. 산림 장부와 일련번호를 섞지 않는다.",
"kind": "logic",
"tables": [
{
"key": "keys",
"name_ko": "열쇠 발급 내역",
"summary": "열쇠마다 처음 낸 판·날짜·목차 코드·이름.",
"shape": "map",
"rows": 1367,
"columns": [
{
"key": "@key",
"name_ko": "건설 공종 열쇠",
"unit": "",
"visible": true,
"is_row_key": true
},
{
"key": "issued_edition",
"name_ko": "처음 낸 판",
"unit": "",
"visible": true
},
{
"key": "issued_on",
"name_ko": "처음 낸 날",
"unit": "",
"visible": true
},
{
"key": "first_toc_code",
"name_ko": "처음 목차 코드",
"unit": "",
"visible": true
},
{
"key": "first_toc_number",
"name_ko": "처음 목차 번호",
"unit": "",
"visible": true
},
{
"key": "first_name",
"name_ko": "처음 이름",
"unit": "",
"visible": true
}
]
}
],
"value_groups": [
{
"key": "note",
"name_ko": "메모",
"summary": "이 벌이 무엇인지 적어 둔 글.",
"kind": "doc"
},
{
"key": "policy",
"name_ko": "방침",
"summary": "이 벌을 다룰 때 지킬 것.",
"kind": "doc"
},
{
"key": "key_prefix",
"name_ko": "열쇠 앞꼬리",
"summary": "산림 FW · 건설 CW.",
"kind": "doc"
},
{
"key": "key_digits",
"name_ko": "열쇠 자릿수",
"summary": "다섯 자리.",
"kind": "doc"
},
{
"key": "next_serial",
"name_ko": "다음 일련번호",
"summary": "다음에 낼 열쇠 번호.",
"kind": "doc"
},
{
"key": "editions",
"name_ko": "판별 자리→열쇠",
"summary": "고시 판마다 목차 자리와 열쇠를 이은 사전. 낱값이 아니라 이음표다.",
"kind": "doc"
}
]
},
{
"file_id": "work_item_axis_policy",
"path": "resources/data_work_item_master/axis_policy.json",
"name_ko": "공종 축 줄 구실 잣대",
"summary": "어느 줄이 공종이고 어느 줄이 총칙·묶는 마디인지 가르는 잣대. ⚠ 지우는 잣대가 아니라 **가르는** 잣대다.",
"kind": "logic",
"tables": [
{
"key": "roles",
"name_ko": "줄 구실 넷",
"summary": "공종·묶는 마디·총칙·빈 잎 — 각각 무엇이고 남기는지.",
"shape": "map",
"rows": 4,
"columns": [
{
"key": "@key",
"name_ko": "구실 이름",
"unit": "",
"visible": true,
"is_row_key": true
},
{
"key": "name_ko",
"name_ko": "한글 이름",
"unit": "",
"visible": true
},
{
"key": "rule",
"name_ko": "가르는 규칙",
"unit": "",
"visible": true
},
{
"key": "keep",
"name_ko": "남기나",
"unit": "",
"visible": true
},
{
"key": "why",
"name_ko": "까닭",
"unit": "",
"visible": true
}
]
},
{
"key": "axes",
"name_ko": "갈래별 잣대",
"summary": "산림·건설 각각의 코드 꼴·열쇠 앞꼬리·총칙 뿌리.",
"shape": "map",
"rows": 2,
"columns": [
{
"key": "@key",
"name_ko": "갈래 이름",
"unit": "",
"visible": true,
"is_row_key": true
},
{
"key": "name_ko",
"name_ko": "한글 이름",
"unit": "",
"visible": true
},
{
"key": "settled",
"name_ko": "확정됐나",
"unit": "",
"visible": true
},
{
"key": "source",
"name_ko": "출처",
"unit": "",
"visible": true
},
{
"key": "toc_source",
"name_ko": "계층 근거",
"unit": "",
"visible": true
},
{
"key": "code_prefix",
"name_ko": "목차 코드 앞꼬리",
"unit": "",
"visible": true
},
{
"key": "key_prefix",
"name_ko": "열쇠 앞꼬리",
"unit": "",
"visible": true
},
{
"key": "general_provision_roots",
"name_ko": "총칙 뿌리",
"unit": "",
"visible": true
},
{
"key": "general_provision_basis",
"name_ko": "총칙 뿌리 근거",
"unit": "",
"visible": true
},
{
"key": "code_shape",
"name_ko": "목차 코드 꼴",
"unit": "",
"visible": true
},
{
"key": "code_shape_why",
"name_ko": "코드 꼴 까닭",
"unit": "",
"visible": true
},
{
"key": "general_provision_pending",
"name_ko": "총칙 미확정",
"unit": "",
"visible": true
},
{
"key": "known_defects",
"name_ko": "아는 흠",
"unit": "",
"visible": true
},
{
"key": "roots_are",
"name_ko": "뿌리를 적는 꼴",
"unit": "",
"visible": true
},
{
"key": "counts",
"name_ko": "줄 구실 셈",
"unit": "",
"visible": true
}
]
}
],
"value_groups": [
{
"key": "note",
"name_ko": "메모",
"summary": "이 벌이 무엇인지 적어 둔 글.",
"kind": "doc"
},
{
"key": "policy",
"name_ko": "방침",
"summary": "이 벌을 다룰 때 지킬 것.",
"kind": "doc"
},
{
"key": "decision_order",
"name_ko": "가르는 차례",
"summary": "총칙 → 공종 → 묶는 마디 → 빈 잎 차례로 본다.",
"kind": "doc"
}
]
},
{
"file_id": "manifest_work_item_master_const",
"path": "resources/data_work_item_master/_manifest_const.json",
"name_ko": "건설 공종 축 폴더 지문",
"summary": "건설 공종 축을 어느 판·어느 원문으로 지었는지 못박는 표.",
"kind": "fingerprint",
"tables": [
{
"key": "files",
"name_ko": "실린 파일",
"summary": "이 폴더에 든 파일과 지문.",
"shape": "list",
"rows": 3,
"columns": [
{
"key": "file",
"name_ko": "파일 이름",
"unit": "",
"visible": true
},
{
"key": "sha256",
"name_ko": "파일 지문(SHA-256)",
"unit": "",
"visible": false
},
{
"key": "size_bytes",
"name_ko": "파일 크기",
"unit": "byte",
"visible": false
}
]
}
],
"value_groups": [
{
"key": "built_by",
"name_ko": "만든 자리",
"summary": "이 벌을 지은 코드나 사람.",
"kind": "doc"
},
{
"key": "source",
"name_ko": "출처",
"summary": "어느 원문에서 왔는지.",
"kind": "doc"
}
]
}
],
"columns": {
@@ -53,7 +53,7 @@
"FP-02"
],
"general_provision_basis": "FP-01 적용기준 51줄 · FP-02 소요재료 및 기계손료 27줄. ⚠ 지우면 안 됨 — `B09_Estimation_Consumables.py` 가 FP-02-01-01·FP-02-01-08(연료 F0042·F0043·F0058)과 FP-02-02-01/02/04/05/06(손료계수 F0064~F0069)을 읽는다.",
"counts_2025_82": {
"counts": {
"rows": 477,
"work_item": 304,
"group": 82,
@@ -96,15 +96,15 @@
"밑수 미확보 447 — 곱하면 안 되는 줄이라 받는 쪽이 가려야 함",
"갈래 키 비어 있음 — 랩탑 메인이 뒤로 미룸"
],
"counts_2026": {
"roots_are": "**장 파일 이름**(「01_공통부문/제1장_적용기준.md」). 뽑는 코드가 목차 코드(`CP-01-01`)로 바꿔 준다 — 관리자가 부문 번호를 셀 필요가 없다.",
"counts": {
"rows": 1367,
"work_item": 997,
"group": 270,
"general_provision": 0,
"empty": 100,
"note": "총칙 표시 전 셈(랩탑 메인 첫 산출). 뿌리를 붙이면 `general_provision` 으로 옮겨 간다 — 줄이 줄지는 않는다."
},
"roots_are": "**장 파일 이름**(「01_공통부문/제1장_적용기준.md」). 뽑는 코드가 목차 코드(`CP-01-01`)로 바꿔 준다 — 관리자가 부문 번호를 셀 필요가 없다."
}
}
}
}
+36 -2
View File
@@ -11,6 +11,7 @@
from __future__ import annotations
import json
import re
from pathlib import Path
import pytest
@@ -21,7 +22,13 @@ LABELS_PATH = ROOT / "resources" / "data_master_labels" / "labels_2026-01-01.jso
#: 브레인 갈래 나눔표(2026-09-15). 폴더 장부(_manifest)는 그 31 밖이라 따로 센다.
#: 2026-09-16 사용자 판정 — `structure_unit_observed`(울진소광 한 현장 관찰값)와
#: `revetment_sabang`(교본값에 설명 글 섞임)은 기초데이터가 아니라 **참고 사례**다.
BRAIN_KIND_COUNTS = {"logic": 15, "base_value": 9, "byproduct": 4, "seed": 1, "reference": 2}
BRAIN_KIND_COUNTS = {
"logic": 19,
"base_value": 9,
"byproduct": 6,
"reference": 2,
"seed": 1,
}
MANIFEST_PREFIX = "manifest_"
META_KEYS = {
@@ -130,7 +137,9 @@ def test_갈래_나눔이_브레인_표와_같다(labels):
continue
counted[entry["kind"]] = counted.get(entry["kind"], 0) + 1
assert counted == BRAIN_KIND_COUNTS
assert sum(counted.values()) == 31
# 2026-09-17 31 → 37 — 공종 축 산출 일곱을 이름표에 올림(건설 마스터·미판정·밑수 · 열쇠 장부 둘 ·
# 구실 잣대 · 건설 폴더 지문). 지문은 manifest 라 이 셈에서 빠진다.
assert sum(counted.values()) == 37
def test_갈래_이름이_다_풀려_있다(labels):
@@ -192,6 +201,31 @@ def test_표_이름이_비지_않았다(labels):
assert not blank, f"한글 이름이 빈 표: {blank}"
def test_이름이_영문으로_떨어진_칸이_없다(labels):
"""⚠ 빈칸이 아니라 **영문 키가 그대로 이름 자리에 앉은** 것을 잡는다(2026-09-17 데스크탑 서브).
새 파일을 이름표에 올릴 때 이름을 못 지으면 키를 그대로 베끼기 쉬운데, 그러면 화면에
`general_provision_roots` 같은 영문이 뜬다. 한글이 한 글자도 없으면 이름을 안 지은 것이다.
"""
hangul = re.compile(r"[가-힣]")
bad = []
for entry in labels["files"]:
for table in entry["tables"]:
for column in table["columns"]:
if not hangul.search(column["name_ko"]):
bad.append(f"{entry['file_id']}::{table['key']}/{column['key']}")
if not hangul.search(table["name_ko"]):
bad.append(f"{entry['file_id']}::{table['key']}")
for group in entry["value_groups"]:
if not hangul.search(group["name_ko"]):
bad.append(f"{entry['file_id']}::{group['key']}")
for merged in labels["merged_tables"]:
for column in merged["columns"]:
if not hangul.search(column["name_ko"]):
bad.append(f"{merged['key']}/{column['key']}")
assert not bad, f"영문으로 뜨는 칸: {bad}"
def test_값_묶음_이름이_비지_않았다(labels):
blank = [
f"{entry['file_id']}::{group['key']}"
+6 -5
View File
@@ -50,12 +50,13 @@ def test_갈래는_이름표_차례_파일_34_강우_캐시는_없음(client: Te
assert [g["key"] for g in groups] == [k for k in LABELS["kinds"]]
counts = {g["key"]: len(g["files"]) for g in groups}
# 2026-09-16 사용자 판정 — 한 현장 관찰값·교본 설명 섞임 둘은 참고 사례로 옮겼다.
# 2026-09-17 공종 축 산출 일곱을 올림 — logic 15 → 19 · byproduct 4 → 6 · fingerprint 3 → 4
assert counts == {
"logic": 15,
"logic": 19,
"base_value": 9,
"byproduct": 4,
"byproduct": 6,
"reference": 2,
"fingerprint": 3,
"fingerprint": 4,
"seed": 1,
}
assert {g["key"]: g["label"] for g in groups}["base_value"] == "기초값"
@@ -83,8 +84,8 @@ def test_트리_표_id_와_이름표_표_id_는_양쪽으로_같음(client: Test
label_only += [f"{entry['file_id']}::{i}" for i in sorted(label_ids - tree_ids)]
assert not_labelled == [], not_labelled
assert label_only == [], label_only
# 2026-09-17 91 → 92 — 공종 마스터가 `toc_duplicate_rows`(목차 번호가 겹친 줄) 를 새로 냄.
assert sum(len(t) for t in files.values()) == LABELS["counts"]["tables"] == 92
# 2026-09-17 92 → 100 — 공종 축 산출 일곱을 이름표에 올림.
assert sum(len(t) for t in files.values()) == LABELS["counts"]["tables"] == 100
for entry in LABELS["files"]: # 이름이 실제로 붙음 · 줄 수도 이름표 셈과 같음
named = {t["key"]: t for t in entry["tables"]}
for t in files[entry["file_id"]]: