main_laptop_1 -> main byeonghap (4 hwangyeong 585 commits) #12

Merged
eomsangdon merged 585 commits from main_laptop_1 into main 2026-09-08 17:26:30 +09:00
585 Commits
Author SHA1 Message Date
eomsangdon 6a90bc1015 Merge remote-tracking branch 'origin/main_desktop_1' into main_laptop_1 2026-09-08 17:04:49 +09:00
eomsangdon 8e99b8a763 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 17:03:13 +09:00
eomsangdon ed37152184 Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-08 17:03:09 +09:00
eomsangdonandClaude Opus 5 635eb69003 chore(db): 프로젝트 통째 복제 도구를 저장소에 둠 (창별 검증 프로젝트용)
⚠ 네 창이 **같은 원본에서 같은 방법으로** 떠야 수치를 견줄 수 있는데,
쓰던 스크립트가 `tmp/` 에 있어 다른 창이 못 봤음(2026-09-08 랩탑 두 창이 막힘).
원본은 SELECT 만 함 — 원본 행·파일에 쓰지 않음.

뜨는 것 — DB 열 표(projects·input_files·processed_point_cloud·surface_models·
routes·route_points·route_statistics·longitudinal_sections·cross_sections·
project_workflow_stages)와 저장소 폴더 통째. 자동증가 id 는 새로 받고 참조를 다시 이음.
파일 안에 박힌 옛 UUID 와 초기값 스냅숏의 surface_model_id 도 치환함.

⚠ DB 만 뜨거나 파일만 떠서는 화면이 안 섬 — 수량은 DB(측점·횡단)를 보고
구조물·관 정본은 파일(structures.json·pipe_points.json)이라 둘 다 필요함.

쓰는 법: ./venv/Scripts/python.exe db_management/tools_clone_project.py <원본 UUID> "<새 이름>"

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 17:01:16 +09:00
eomsangdon 394b727c95 Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-08 16:48:48 +09:00
eomsangdon 5f0a45b555 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 16:48:47 +09:00
eomsangdonandClaude Opus 5 bbdae2d99c knowledge(미결): 옆도랑 저폭 미결 22 에 실물 근거 1건 추가
소광 기번8 내역서 「제형돌수로 B=0.75 m」 — 별표2 너비 0.5~1 m 범위 안.
한 공사지 한 줄이라 일반화 불가 표시 유지.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 16:10:34 +09:00
eomsangdon 337e6a69c1 Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-08 16:09:34 +09:00
eomsangdonandClaude Opus 5 385f5c303d fix(B08): 씨앗뿜어붙이기 밑수 ㎡ 추가 + 시공량 모양을 밑수로 안 받게 막음
⚠ 아홉 중 여덟째 — FP-05-24 는 원문 L2769·L2794 절 머리 아래 「(㎡당)」인데
「단위:」 글자 없이 **괄호만** 있어 마스터가 못 읽던 자리(원문 열어 확인).

⚠ 그리고 매핑 밑수 칸은 **사람이 손으로 적는 자리**라, 품셈에 섞여 있는
「(㎥/1대, 1일)」 같은 **시공량**을 적을 수 있음 — 그것은 밑수의 **역수**라
받으면 그 공종이 조용히 뒤집힌 단위를 가짐. `is_quantity_unit()` 으로 좁게 막음
(나눗셈·쉼표가 있거나 「일당·조·인」이면 안 받음). 애매하면 안 받는 쪽 —
안 받으면 대조가 없을 뿐이고, 잘못 받으면 틀린 단위로 검사를 통과시킴.

시험 741 통과 (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 14:18:06 +09:00
eomsangdonandClaude Opus 5 c81c11e330 refactor(B08): Engine_Handoff 1,279줄을 셋으로 가름 (700줄 제한)
⚠ **계약은 하나도 안 바뀜** — 줄의 모양·칸 이름 그대로이고 파일만 가름.
종전 이름으로 부르던 곳이 그대로 돌도록 `Engine_Handoff` 가 다시 내보냄.

  Engine_Handoff.py          249줄  build_handoff·검사·다시 내보내기
  Engine_Handoff_Mapping.py  413줄  매핑표·WorkItemMapping·갈래표·묶음 전개
  Engine_Handoff_Rows.py     700줄  줄 빌더 여덟(토공·운반·구조물·준비공·배수관·연장·타설)

확인 — 시험 739 통과로 가르기 전과 같음(B05 코리도 1건 기존 깨짐, 무관).
다시 내보내는 이름 22개를 실제로 불러 봄.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 14:16:17 +09:00
eomsangdonandClaude Opus 5 5063bd376b feat(B09): 「단위:」 없이 괄호만 적는 절도 읽음 — 단위 빈 일위대가 61 → 27
- 「(㎡당)」·「(인/100㎡당)」·「(1,000본당)」처럼 「단위:」를 뺀 절이 있음.
  씨앗뿜어붙이기 5-24 가 그 모양이라 통째로 미확보였음(메인이 물은 아홉 중 하나).
- 「인/」 앞머리는 품의 단위라 건너뜀 — 「인/㎡당」은 「㎡당 몇 인」이라 공종 단위가 ㎡.
- ⚠ 「㎥/1대, 1일」·「대/ton」은 안 받음 — 그건 시공량이지 소요량이 아니라
  공종 단위로 읽으면 뜻이 뒤집힘(역수). 밑수가 둘인 「1ha당, 100본당」도 안 받음.
- 절 단위 128 → 185, 단위 빈 일위대가 61 → 27. 내역 금액 변동 없음.

시험 2건 추가(받을 것·막을 것 짝), 254건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 14:15:33 +09:00
eomsangdonandClaude Opus 5 510337557b fix(B08): 자재 이름 「물구멍」 → 「물구멍관」 (카탈로그 규약)
자재 카탈로그가 이름+규격 두 문자열로 줄을 찾고, 그 규약이 「공백 없는 한 낱말」임
(2026-09-08 B09 실측 — 키는 나라장터 물품식별번호라 우리가 맞출 것이 아님).
전개·관측 원단위표·할증표 세 곳을 함께 고침 — 한쪽만 고치면 이름이 갈림.

⚠ 고임돌·야면석 규격에 뒷길이를 넣지 않음 — 뒷길이는 **수량이 갈리는 축**이지
단가가 갈리는 축이 아님(야면석 톤당 단가는 뒷길이와 무관). B09 가 앞 제안을 물림.

곁들여 — 물구멍관 길이 0.5 가 구조물마다 다른 값임을 주석에 적음
(옹벽 0.32 m/m · 돌기슭막이 0.39 m · 돌골막이 0.5 m/개소, 실무 관측).

시험 739 통과 (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 14:13:42 +09:00
eomsangdonandClaude Opus 5 66099e79a0 data(B08): 원문 대조로 밑수 단위 일곱을 매핑에 채우고, 덤프 단위중량 근거 기록
⚠ 마스터가 못 채운 자리(절 머리에 「(단위: …)」가 없는 공종)를 원문에서 읽어 적음.
줄 번호까지 열어 확인했고, 시험이 그 줄에 그 글자가 있는지 매번 대조함.

  FP-09-03-02 기계(흙깎기) ㎥ L4704 · FP-09-04 암절취 ㎥ L4725·L4738
  FP-09-05 발파암 ㎥ L4744·L4777 · FP-09-16 노체 ㎥ L5322·L5336·L5363
  FP-09-17-01 비탈면 다짐 ㎡ L5368 · FP-10-11 불도저 운반 ㎥ L5919
  FP-10-12 덤프 운반 ㎥ L5928·L5971·L5985

⚠ FP-12-06 은 「없음」이 아니라 **거절** — L6229 「(단위: 일당)」은 품의 단위이지
공종 수량 단위가 아님. 받으면 틀린 단위로 검사를 통과시킴. 매핑에 사유를 적음.

⚠ 덤프 단위중량 γt 가 원문에 있었음(「현장값이라 미확보」 정정) —
L5930 [주]② 「토사 1.9ton/㎥, 암절취 및 발파암은 2.4ton/㎥」. 갈래별로 값이 다름.

시험 739 통과 (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 13:25:58 +09:00
eomsangdonandClaude Opus 5 843b62cca9 feat(B09): 원문 단위 읽기 넓힘 — 상위 절·수 낀 표기까지, 대신 장 단위는 막음
- 「A = 77.7 ㎡/시간」처럼 등호와 단위 사이에 수가 끼는 표기를 읽음
  (9-17-1 비탈면 다짐이 그래서 통째로 미확보였음).
- 상위 절이 스스로 안 적고 하위만 적는 경우를 채움 — 암절취 9-4 · 발파암 9-5 ·
  노체 9-16 · 덤프운반 10-12 가 전부 ㎥.
- ⚠ 좁힘: 말한 하위가 둘 이상이고 값이 하나로 모일 때만. 하나만 말한 것을 상위에
  주면 장 전체가 그 단위가 됨 — 「13. 돌공사」가 실제로 ton 으로 나왔음.
- 단위 빈 일위대가 73 → 61.

시험 3건 추가, 252건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 13:22:27 +09:00
eomsangdonandClaude Opus 5 027e059ab8 fix(B08): 층따기를 면적으로 안 내보내도록 막고, 매핑이 적은 밑수까지 대조
⚠ 또 하나의 단위 불일치 — 층따기 9-18 은 [주] 공식 `Q1 = 3600×q×K×f×E/㎝ = ㎥/시간`
이라 단가가 ㎥당인데 우리는 성토 비탈면적 ㎡ 2,645.21 을 보내고 있었음.
그대로 곱혀 4,102,708원이 서 있었음(B09 실측).

- 매핑에 `basis_unit`·`basis_source`·`mismatch_reason` 을 두고, 우리 단위와 뜻이
  다르면 **환산하지 않고** 막음(`input_missing` — 층따기 단 높이·폭은 설계 입력).
  막힌 줄에도 집계값을 `spec_detail` 에 남겨 되짚게 함.
- ⚠ 절 머리에 「(단위: …)」가 없는 공종은 마스터 `basis_unit` 이 빔(477 중 167만 참).
  그래서 밑수 대조가 조용히 통과하고 있었음 — 매핑이 원문에서 읽어 적은 밑수를
  `declared_units()` 로 함께 넘겨 대조가 서게 함.
- 성토면다짐(9-17-1)은 시공량이 ㎡/시간이라 면적이 맞음 — 좁게 막아 형제 공종은 그대로.

시험 736 통과 (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 13:16:30 +09:00
eomsangdonandClaude Opus 5 189108e1f7 feat(B09): 공종 단가 기준 단위를 품셈 원문에서 채움 — 층따기 단위 불일치 드러남
- 일위대가 211개 중 73개가 단위를 못 달고 있었고, 단위가 없으면 내역서의 단위
  불일치 검사가 못 걸림. 층따기가 「㎡ 수량 × ㎥당 단가」로 4,102,708원을 내고
  있었음 (돌쌓기 「m × ㎡당」과 같은 병).
- 원문에 적힌 자리 둘에서만 읽음 — 절 머리 아래 「(단위: ㎥당)」 · 공식형 [주]의
  「Q= ㎥/시간」. 73개 중 12개를 채웠고 나머지는 미확보로 둠.
- ⚠ 「인 당」·「일 당」은 품의 단위이지 공종 단위가 아니라 받지 않음. 틀린 단위로
  검사를 통과시키면 없느니만 못함.
- 자원 줄이 단위를 주면 그 값이 이김 — 원문 조회는 빈 자리만 채움.
- 결과: 층따기 4,102,708원이 「단위가 안 맞습니다」로 바뀜. 본체 합계
  7,837,891 → 3,735,183원. ⚠ 줄어든 것이 맞음 — B08 이 ㎥ 로 보내야 풀림.

시험 6건 추가, 249건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 13:09:27 +09:00
eomsangdon 188bcfb78c Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 12:49:27 +09:00
eomsangdon 47943f2990 auto: 2026-09-08 12:48 (EOMSANGDON-HOME) 2026-09-08 12:48:34 +09:00
eomsangdonandClaude Opus 5 81af27f0e8 fix(B08): 돌쌓기 인계를 품셈 밑수(㎡)로 세도록 정정 + 밑수 단위 대조 가드 신설
⚠ 실제로 금액이 틀리던 자리 — 돌쌓기(찰)를 `m · 10.0` 으로 보내는데 품셈
13-4-5 밑수는 ㎡ 라, 받는 쪽이 52,938.9원/㎡ 를 그대로 곱해 529,389원이 섰음.
면적으로 세면 26.101㎡ × 52,938.9 = 1,381,753원 — 2.6배. 양쪽 다 오류 없이 조용했음.

- `billing_component` 를 매핑에 두고 **전개 성분에서** 내역 수량을 가져옴
  (면적 식을 두 벌로 짜지 않음). 돌쌓기·큰돌쌓기 = ㎡ · 비탈면적.
- 성분이 안 서면 **연장으로 대신 세지 않음** — 0 + 품셈 밑수 단위로 두고 사유를 냄.
- 신설 `B08_Quantity_Engine_BasisUnit.py` — 인계 줄 단위를 **공종 마스터 밑수**와
  대조해 `basis_unit_warnings` 로 냄. 표기 차이(㎥/m3·개소/개)만 흡수하고 **환산 안 함**.
- 돌 종류를 둘째 갈래 축(`secondary_axes`)으로 실어 보냄 — 품셈 13-4 [주]② 가
  「깬돌·깬잡석 전용」이고 돌 종류 표는 13-5 에 따로 있음. 공종 판단은 사용자 확정 대기.
- 옛 시험이 「10.0 m」를 계약으로 못 박고 있어 정정(오늘 다섯 번째).

시험 732 통과 (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:48:29 +09:00
eomsangdonandClaude Opus 5 4e59afb0f2 fix(B09): 한 표에 밑수가 둘인 표를 밑수마다 갈라 세움 — 막돌 채집 3.8배 오류
- 품셈 13-2 채집 계열은 같은 품을 「㎡당 0.17 / ㎥당 0.64」 두 벌로 줌.
  행-자원으로 읽어 앞줄만 잡고 뒷줄을 버려, 막돌 채집이 ㎡당 값을 ㎥ 단위로
  달고 있었음 (29,251.5원/㎥ ← 실제 ㎥당은 110,123.5원).
- 밑수마다 따로 세우고 갈래 이름에 밑수를 넣음(#㎡당·#㎥당). 갈래의 단위가
  그 밑수라 내역서 단위 불일치 검사가 엉뚱한 밑수를 걸러 냄.
- 밑수 표기가 하나뿐인 표는 안 건드림 — 넓게 잡으면 멀쩡한 표가 쪼개짐.
- 열이 갈래인 표는 갈래 × 밑수로 폄(「45㎝·㎥당」).
- 이름을 못 풀면 표째 버림 — 앞줄만 서는 반쪽 단가를 만들지 않음.

⚠ 야면석 채집(13-2-4)은 아직 안 섬 — 직종을 「인 부」로만 적는데 그런 직종이
노임 카탈로그에 없음(품셈 전체에서 이 표 하나뿐). 보통인부로 단정하지 않고
사용자 확정 대기로 둠.

시험 5건 추가, 243건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:45:22 +09:00
eomsangdon c8cdf8ead2 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 12:36:22 +09:00
eomsangdon e55ff8a82e Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 12:35:36 +09:00
eomsangdonandClaude Opus 5 48173b8ca4 fix(B08): 뒷길이를 접지 않음 — 일곱 규격을 다 씀
조율 창 지시로 KeyError 를 막으러 갔다가 더 나쁜 것을 찾음.
죽지는 않고 있었고 대신 조용히 접히고 있었음.

  40㎝  → 45㎝ 계수로 돎     999㎝ → 60㎝ 계수로 돎
  25·30 → 35 로, 75 → 60 으로 접힘

⚠ 접을 까닭이 없었음 — 품셈 13-4-3·13-4-4 [주]① 이 25·30·35·45·55·60·75
  일곱 규격을 다 줌. 우리 표가 네 칸만 들고 있었을 뿐임.

- 표를 일곱 규격으로 채움(건설품셈 참고자료 값, 고임돌은 「깬돌」 열)
- 표에 없는 값(40 등)은 접지 않고 「품셈 표에 없어 물량이 서지 않습니다」
- ⚠ 원문 「-」인 칸은 0 줄을 만들지 않음 — 0 은 「없음」과 구별이 안 되고
  받는 쪽이 「값이 0 인 자재」로 읽음
- ⚠ 고른 종류의 빈 칸은 빈 칸으로 덮음 — None 이라고 안 덮었더니
  「야면석 75㎝」처럼 원문에 「-」인 칸에 종전 값(깬돌 0.25)이 섰음(만들다 잡음)

⚠ 옛 시험이 「덮는 위 칸으로 접는다」를 계약으로 못 박고 있었음 — 오늘 네 번째.

시험 넷 — 접지 않음 · 표에 없으면 물량 안 냄 · 고른 종류의 빈 칸은 줄 없음 ·
일곱 규격이 다 섬.
시험: 723 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:33:31 +09:00
eomsangdonandClaude Opus 5 3a452028d3 feat(B09): 수량 자릿수를 품셈 1-2-2 종목별로, 단위 불일치는 금액을 안 만듦
수량 자릿수 (품셈 1-2-2의 1)
- 화면이 모든 수량을 2자리로 찍고 있었음. 품셈은 종목별로 자리를 따로 정함 —
  체적합계·시멘트·철근 정수 / 돌쌓기·옹벽·떼 1자리 / 철강재 3자리.
- 수량은 반올림([주]①), 금액은 버림 — 반대라 모듈을 갈라 둠.
- 항목이 표를 이김([주]②) → override 받음. 표에 없는 종목은 안 건드림.
- 서버가 줄마다 quantity_digits 를 실어 보내고 화면은 그 자리로 찍기만 함.

금액 자리 보강 (품셈 1-2-2의 2)
- 설계서 총액 1,000원 미만 버림 · 일위대가 계금 1원 미만 버림 추가.
  금액란(0.1원)·소계(1원)와 자리가 넷 다 다름.

⚠ 단위 불일치 가드
- 돌쌓기(찰)이 연장 10m 로 오는데 일위대가는 ㎡당이라 52,938.9×10 = 529,389원이
  조용히 서 있었음. 면적으로 세면 1,381,753원 — 2.6배 적은 금액이었음.
- 단위가 다르면 곱하지 않고 사유를 남김. 표기 차이(㎥/m3·개소/개)는 흡수하되
  환산은 안 함. 내역 합계 6,252,172 → 5,048,775 (틀린 두 줄이 빠짐).

제잡비 밑수
- 닫힌 물음임을 원문 인용으로 못 박음 — 산림품셈 [주]③ 여섯 자리가 「노무비의
  합계액」이고, 일위대가 안에는 간접노무비가 없으므로 그 표의 노무비 합 =
  직접노무비. 하루에 두 번 뒤집힌 값이라 세 번째가 없게 근거를 남김.

시험 10건 추가, 238건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:33:23 +09:00
eomsangdonandClaude Opus 5 e9a25bbf87 feat(B08): 돌 종류 축을 이음 — 계수 셋이 그 축으로 갈림
랩탑이 stone_kind 칸을 만들어 축이 생김. 지금까지 계수 셋이
축 없이 한 벌(깬돌 계열)로만 돌고 있었음.

원문 근거 셋을 데이터 파일로 뺌 (resources/data_masonry/stone_kind_*)
  고임돌      품셈 13-4-3 · 돌 종류 네 줄(야면석/깬잡석/깬돌/견치돌)
  채움콘크리트  품셈 13-4-4 [주]① · 두 줄(야면석 33.3% / 깬돌 계열 45%)
  뒤채움      교본 7-3 · 깬돌·잡석 1/2 · 야면석 1/3

⚠ 안 고르면 종전 값 그대로 — 건설품셈 참고자료 한 벌로 돌되 그 사실을 알림.
  값을 못 낸다고 멈추면 이미 저장된 프로젝트가 통째로 빔.
  (실측: 미지정 고임돌 3.915 · 채움콘 5.220 · 막자갈 15.130 — 고치기 전과 같음)

⚠ 만들다 잡은 것 — 표는 「뒤채움 몫」인데 우리 식은 「빼는 몫」이라 뜻이 반대.
  그대로 넣었더니 미지정 막자갈이 15.130 → 19.045 로 바뀌었음. 1 에서 빼게 고침.
  종전 하드코딩 2/3 이 곧 야면석(1 − 1/3)이었음.

⚠ 지어내지 않는 자리 — 원문 「-」(견치돌 25·30 · 야면석 75)는 null 로 두고
  사유를 냄. 모르는 종류도 조용히 안 넘김.

시험 7건 — 미지정이 종전 그대로 · 야면석이 바뀜 · 깬돌이 종전과 같음 ·
⚠ 뒤채움 몫을 뒤집어 쓰지 않음 · 「-」를 안 지어냄 · 모르는 종류 · 근거 문구

시험: 720 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:28:42 +09:00
eomsangdon e6a6fa551a Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 12:23:57 +09:00
eomsangdon c129b04f8c Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-08 12:23:56 +09:00
eomsangdonandClaude Opus 5 0740ef4c15 knowledge(품셈): 건설품셈 개정사항 그림 4장 복원 — 돌쌓기 표준도 포함
md 가 ../pic/*.png 를 네 곳 참조하는데 그 폴더가 저장소에 없었음.
원본 PDF 는 있어서 그대로 뽑음(지어낸 것 없음).

  p35_1  686×673
  p39_1  690×673
  p64_1  368×229   ← [참고자료] 돌쌓기 표준도
  p98_1  592×168

⚠ p64 가 B07 표준도 일감의 근거임 — 우리가 그리려던 그림이
품셈 참고자료로 이미 있었음. 같은 자리의 「돌쌓기 규격별 소요량」 표가
우리 계수(고임돌 0.15 · 채움콘크리트 0.20 · 모르터 0.009)의 원천이라
그림과 표가 한 벌로 표준도 한 장이 그대로 나옴.

그림에 적힌 것: 뒷길이 · 뒷채움 · 채움재(콘크리트 또는 잡석) · 기초
— 우리 전개식 성분과 그대로 맞물림.

⚠ 2026 개정안이 그 참고자료를 「삭제 검토」로 두었음. 없어지면
우리 계수의 근거가 사라지므로 어느 판을 정본으로 쓸지 정해야 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:22:51 +09:00
eomsangdonandClaude Opus 5 6f87a947ca feat(B05): 돌쌓기 「돌 종류」 입력칸 신설 + 측구 저폭 미결 등재
- masonry_wet·masonry_dry 에 `stone_kind` 옵션 추가 (야면석·호박돌 / 깬잡석 /
  깬돌 / 견치돌). 기본값 없음·required·phase detail — 품셈 13-4-3(고임돌)·
  13-4-4(채움 콘크리트) 가 돌 종류로 갈리므로 B08 이 계수 축을 하나로 고를 수 있게 함.
- 값 선정은 설계 판단이라 기본값을 두지 않음 — 미지정이면 B08 이 드러냄.
- 지식DB 미결 22 등재: 옆도랑 저폭 0.3m 근거 공백(별표2 는 너비·깊이만) +
  현행 45° 단면이 통수단면 경제 단면 60° 와 어긋남.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 12:22:21 +09:00
eomsangdonandClaude Opus 5 b310623468 feat(B09): 불도저 단(gear)별 속도표·보정식 읽어 비다짐(정지) 세움
- 8-2-1 속도표를 단(1·2·3단)까지 전부 담음. 무한궤도 13톤·타이어의 없는 후진 3단은
  담지 않음 — 없는 속도로 싸이클이 서면 값이 조용히 틀림.
- 「(0.55-0.1)」 같은 보정식을 읽음 (8-2-1 [주]④ 정지작업 −0.1). 범위 표기와 갈라
  다룸 — 범위는 여전히 확정값 아님.
- 갈래 없는 표(「f | 1/1.3」)도 읽음.
- 결과: FP-09-17-02 비다짐(정지) 1,410.1 원/㎥ 섬 (3단 75/98, E=0.45, 19톤).
- 시험 4건 추가, 228건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 11:57:32 +09:00
eomsangdonandClaude Opus 5 b3c6f29684 refactor(B09): 불도저를 딴 파일로 분리, 장비 붙이는 자리를 한 켤레로 맞춤
- B09_Estimation_MachineProductivity_Dozer.py 신설 (8-2-1 전용).
  굴착기(8-1-4)와 한 파일에 두면 700줄을 넘고, 두 식이 섞여 읽힘.
- 굴착기 쪽 장비 붙이기를 attach_machine_share 로 올려 불도저 쪽
  attach_dozer_share 와 같은 모양으로 맞춤 — 자리를 나눠 쓰는 것이 눈에 보이게.
- 줄수: UnitPrice 652 · MachineProductivity 360 · Dozer 398.
- 224건 통과 (동작 변화 없음).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 11:54:12 +09:00
eomsangdonandClaude Opus 5 b97eb042b6 feat(B09): 불도저 표(8-2-1) 읽어 갈래별 단가 세움
- FP-10-11 불도저 운반이 굴착기 식(8-1-4)으로 읽혀 「K·Cm 없음」으로 잘못
  진단되던 것을 8-2-1 식으로 읽게 함. 갈래 셋(토사·파쇄암·발파암) 각각 섬
  (1,439.7 / 3,289.3 / 3,959.4 원/㎥).
- 표가 기종 이름을 안 적어 삽날 용량 q゚ 3.2㎥ + 속도 55/70(2단)으로
  불도저(무한궤도) 19톤을 되짚음. 하나로 안 좁혀지면 안 고름.
- 공식표에만 나오는 기종을 시간당 사용료 층에 넣음 — 안 넣으면 공식이 다 서고도
  붙을 사용료가 없어 빈 일위대가가 남았음.
- 아무것도 안 붙은 제목은 걷어 냄 (화면에서 「조립 불가」로 터지던 자리).
- 시험 6건 추가, 224건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 11:47:51 +09:00
eomsangdonandClaude Opus 5 3c5b561491 feat(B09): 불도저 시공능력 공식 — 굴착기와 다른 식
운반 계열 착수. 품셈 원문 대조 결과 **셋이 서로 다른 식**임

```
굴착기  Q = 3600 ÷ Cm(초) × q · K · f · E        (8-1-4, 이미 구현)
불도저  Q = 60 ÷ cm(분) × (q₀ × e) · f · E        (8-2-1)  ← 이번
        cm = L/V1 + L/V2 + t,  t = 기어 변속 0.25분
덤프    Q = 60 ÷ cm × q · f · E,  q = T·L/γt      (8-2-8)  ← 다음
```
- ⚠ **밑수가 60(분)** 임 — 굴착기(3600초)와 섞으면 **60배 어긋남**. 주석에 못 박음
- ⚠ 속도가 「단」에 따라 다름(8-2-1 [주]) — 굴착·운반 1단 · **흐트러진 토사운반 2단** ·
  평탄 정지 3단. **임도 운반은 [주]② 가 가장 가까워 2단 잠정 채택**(TODO 미결)
- 표에 없는 규격은 **속도를 지어내지 않음**(짝 시험)

검산: 19톤·L=20m·V1 55·V2 70 → cm 0.8994분 · **Q 86.709㎥/hr** · 1㎥당 0.01153hr

검증: pytest 218 통과(신규 3)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:42:05 +09:00
eomsangdonandClaude Opus 5 dd23c9d04f feat(B09): 기계만 쓰는 공종이 공식만으로 서게 함 — 층따기 살아남
「미판정 77건」을 훑다 나온 것 — 그중 일부는 **미판정이 아니라 시공능력 공식표**였음
```
F0287 9-18 층따기        굴착기 0.7㎥ · K 0.9 · f 1/1.3 · E 0.7 · Cm 22
F0328 11-4 쇄석·혼합석 부설  같은 모양
```
- ⚠ **자원 줄이 하나도 없으면 조립 반복문이 그 공종을 아예 안 보고 있었음** —
  표에 인력 줄이 없는 기계 전용 공종이 그 자리. 공식이 온전하면 세우게 고침
- 층따기 **1,550.9원/㎡** · 쇄석 부설 **3,549.5원/㎡** 로 섬 (일위대가 203 → 206)
- 내역서(2f940d8a) 합계 **2,149,464 → 6,252,172원** —
  층따기 2,645.21㎡ × 1,550.9 = 4,102,708 이 실제로 붙음
- ⚠ 짝 시험 — 자원도 공식도 없으면 **여전히 안 섬**(0 원 일위대가 금지).
  그 문이 넓으면 빈 제목이 무더기로 서고 화면이 「단가가 있다」고 거짓말함

검증: pytest 215 통과(신규 2)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:37:50 +09:00
eomsangdonandClaude Opus 5 6aa8001780 fix(B09): 「성분이 빠져 못 세움」을 「일위대가 없음」과 갈라 냄
메인 창이 물음 — 종단배수 셋이 같은 모양(밑수 1 m)인데 맹암거만 금액이 서고
산마루측구·소단측구는 안 서는 까닭

**표 성격이 달랐음** — 맹암거(F0345)는 수량이 실제로 적힌 소요량표라 자원 4줄이
붙어 25,492.1원/m 로 섬. 나머지 둘은 **수량 칸이 비어 있고 참조로만** 적힘
```
FP-12-09-02  철근가공조립 | ton | (빈칸) | 「철근가공조립(간단)의 30%」
FP-12-09-03  거 푸 집    | 회  | (빈칸)
```
⇒ 값을 지어낼 수 없어 **금액을 안 만드는 것이 맞음**(지금 방침 그대로)

- 다만 화면 사유가 **「일위대가 없음」으로 뭉뚱그려지고** 있었음 — 「아직 안 만든 것」과
  「성분이 빠져 못 세운 것」은 **할 일이 다름**. 사유를 갈라 적음:
  「성분이 빠져 단가를 못 세웠습니다 — 철근가공조립 줄을 못 풀었습니다」
- `UnitPriceBuild.component_gaps` 로 자원 축의 사유를 화면까지 나름
- 짝 시험 — 성분 빠진 쪽은 금액 없음 + 사유 표시, **맹암거는 그대로 금액이 섬**

검증: pytest 213 통과(신규 1). 맹암거 40m × 25,492.1 = 1,019,685원 —
메인 값과 일치

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:32:37 +09:00
eomsangdonandClaude Opus 5 dbe761a303 feat(B08): B군 줄에 「어디부터 어디까지」를 실음 — spans 받음
랩탑이 spans 를 넣어 줘서 B군 산출근거가 채워짐.
겹침을 지운 뒤의 구간이라 그 합이 곧 length_m 임.

- station_from/to 에 처음 시작·끝 끝을 싣고
- 비고에 구간을 전부 적음 「구간 10~20m · 50~60m · 90~100m」
  ⚠ 사이가 빈 것을 숨기지 않음 — 처음과 끝만 보면 10~100 을 통으로 오해함
- 겹친 경우는 그 설명이 뒤에 붙음
  「구간 80~140m · 입력 구간 합 80m 에서 겹친 20m 를 뺀 값」

⚠ 측점 표기(NO.4+0.0)는 만들지 않음 — 측점 간격을 아는 화면 몫이고,
  두 벌로 짜면 간격이 바뀔 때 한쪽만 고쳐짐

시험 넷 — 구간이 비고에 적히고 측점이 실림 · 여럿이면 처음·끝을 싣고 전부는 비고에 ·
⚠ 구간 합이 수량과 같음(갈리면 어딘가 틀림) · ⚠ spans 가 없어도 줄은 섬(옛 자료)

시험: 713 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:28:35 +09:00
eomsangdon 9b7dc0796b Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-08 10:27:05 +09:00
eomsangdonandClaude Opus 5 d14cd46f51 feat(수량): 구조물 연장에 **겹침을 지운 구간 목록**을 함께 냄
B08 창 요청(2026-09-08) — 연장(m)만 내면 산출근거에 **「어디부터 어디까지」**를 못 적음.
실무 내역은 「산마루측구 40m」 한 줄이라 없어도 서지만, 있으면 검산이 쉬워짐.

`_merge` 가 길이 대신 **합친 구간 목록**을 돌려주게 하고, 행에 `spans`
(`[{start_m, end_m}, …]`)를 실음. **길이는 그 목록의 합**이라 둘이 갈릴 수 없음.

⚠ 겹침은 **합쳐서** 냄 — 데스크탑 실측: 산마루측구 둘(80~120 · 100~140)이 **60.0m**
로 합쳐져 내역서에 감(단순 합 80.0m). 구조물별로 셌으면 **20m 를 더 셀** 자리였음.
그 규칙이 이 함수 한 벌에만 있어야 하는 까닭이기도 함(측구 제외·관 소관 제외도 같음).

시험 5건 — 안 겹치면 그대로 · 겹치면 합침(그 실측 그대로) · 맞닿은 두 구간은 하나 ·
품은 구간은 사라짐 · **행의 길이가 구간 합과 같은지**. 전체 513 passed · 17 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:26:03 +09:00
eomsangdonandClaude Opus 5 d3a763d9c9 feat(B08): B군 종단배수를 연장표로 받음 — 겹친 구간을 두 번 안 셈
랩탑 제안(두 창 합의). common_util_structure_lengths 가 이미 규칙 셋을 갖고
있는데 B08 이 structures.json 을 직접 읽어 세면 그것을 두 벌로 짜게 됨.

그 함수가 갖고 있는 것
  ① 겹친 구간 합치기 (같은 시설을 겹쳐 놓으면 단순 합은 두 번 셈)
  ② 측구 제외 (design_owner — 횡단이 이미 ditch_area_m2 로 셈)
  ③ 관 소관 제외 (managed_by)

⚠ B군만 그 함수로 감 — C군(돌쌓기·옹벽)은 그대로 구조물별 줄
  그 함수는 종류별로 뭉쳐 내는데, C군은 측점·규격이 줄마다 다르고 자재가
  줄마다 나옴. 실무 내역도 B군은 「산마루측구 40m」 한 줄, C군은 구조물별 줄

실측: 겹친 산마루측구 둘(80~120 · 100~140) → 80m 이 아니라 60m
      비고 「입력 구간 합 80m 에서 겹친 20m 를 뺀 값」

⚠ 만들다 낸 것 하나 — 겹침 설명을 blocked_reason 에 넣었더니 B09 가
「막힌 줄」로 읽어 금액을 안 붙였음. 비고(spec_class_basis)로 옮김.
막힌 사유는 진짜 막힐 때만(공종 못 이음 · 연장 0).

시험 다섯 — 종류별 한 줄 · 겹침이 합쳐짐 · ⚠ 겹침 설명은 막힘이 아님 ·
⚠ 안 겹치면 원합을 안 실음(반영률로 오해됨) · 공종 못 이으면 사유와 함께 막힘

시험: 709 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:22:52 +09:00
eomsangdonandClaude Opus 5 cbf991d5be feat(B08): B군 종단배수를 공종에 이음 — 연장이 금액까지 감 (V-3)
랩탑이 「연장은 나오는데 그 뒤가 안 봤다」고 해서 세 걸음을 한 번에 봤음.
끊긴 자리가 둘이었음.

① 매핑에 B군이 하나도 없었음 — 연장 40.0m 은 인계까지 오는데
   공종코드가 비어 있었음. 품셈에 있는 셋을 이었음
     산마루측구 FP-12-09-02 · 소단측구 FP-12-09-03 · 맹암거 FP-12-10
     셋 다 밑수 1 m — 구조물 연장이 그대로 수량
   도수로·절토사면 배수로는 품셈에 그 공종이 없어 pending_user 로 남김
   (마스터 전수: 「수로」로 찾아도 나오는 것은 위 셋뿐)

② ⚠ 「전개식이 없다」로 단정해 B09 가 금액을 안 붙이고 있었음
   B군은 연장이 곧 수량이라 원단위 전개가 필요 없는데,
   blocked_of 가 「성분이 없으면 formula_missing」으로 봤음
   ⇒ 공종코드가 붙었고 수량이 있으면 막힌 것이 아님

실측(맹암거 40m): 0원 → 1,019,685원. 산마루·소단측구는 B09 일위대가 대기.

⚠ 그 김에 확인된 것 — verify_bill_flags 가 「코드 없이 내역에 서는 줄」을
제대로 잡고 있었음(도수로·절토사면 배수로 둘).

시험 셋 — 연장으로 서는 공종은 안 막힘 · ⚠ 코드 없으면 여전히 막힘 ·
⚠ 연장 0 이면 코드가 있어도 막힘 (둘 다 0원 줄을 막는 짝).
시험: 704 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:13:16 +09:00
eomsangdonandClaude Opus 5 1ad40fa2fb fix(B08): 관 줄의 막힌 사유를 둘로 가름 — 「저장하면 풀림」과 「눌러도 안 풀림」
랩탑 [저장] 뒤 실측하니 관 9개 중 6개 값이 서고(59.0m) 3개가 막힘.
그 셋에 「[저장]을 한 번 누르면 섭니다」가 떴는데 눌러도 안 풀리는 자리였음
— cross_sections 에 그 관 자리 측점이 아예 없음(관은 놓였는데 횡단이 안 만들어짐).

- 측점 목록을 함께 받아 「횡단이 있는데 길이만 없음」과
  「관 자리에 횡단이 없음」을 갈라 말함
- ⚠ 관 자리 판정을 아주 좁게(0.05m) — 실측에서 관 439.55 근처에 측점 440.0 만
  있었고, 길이 찾기와 같은 0.5m 로 보면 「횡단이 있다」로 읽혀 거짓 안내가 떴음.
  길이는 B06 이 관이 놓인 측점에만 실으므로 뜻이 다름

실측(5601e828): 값 6줄 59.0m · 막힘 3줄이 정확히 그 셋
  85.05→10.0 · 258.12→11.0 · 534.49→9.0 · 804.30→9.0 · 901.24→10.0 · 984.18→10.0

⚠ npm run build:server-calc 돌려 새 번들로 바꿨음 — 옛 번들은 이웃 측점에도
길이를 실어 같은 관을 두 번 셀 수 있었음(랩탑이 고쳐 push 한 건).

시험 4건 추가(가른 두 문구 · 목록 없으면 종전대로 · ⚠ 옆 측점은 「있음」이 아님).
시험: 701 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:40:51 +09:00
eomsangdon 50c0d234e9 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-08 09:37:29 +09:00
eomsangdonandClaude Opus 5 8555e693ac fix(B06): 관 길이를 **소유 측점에만** 싣게 — 같은 관을 두 번 셀 뻔한 자리
`pipe_length_m` 을 넣고 실화면 [저장]으로 확인하다 드러남 — **관 9개인데 값이 10곳**에 실렸음.
까닭은 `attach_culvert_sets` 가 세트를 **폭의 절반까지 옆 측점에도** 붙이기 때문임(3D·카드가
이어 그리려고). 그 자리에 길이를 실으면 수량이 **같은 관을 두 번** 셈.

- 스펙에 **놓인 누가거리(`chainage_m`)** 를 함께 얹음 — 파이썬 `attach_culvert_sets` ·
  TS `attachCulvertSets` **둘 다**(한쪽만 얹으면 세트 거울 시험이 깨짐. 실제로 깨져서 알았음).
- 길이는 그 값과 측점이 같을 때만 실음.
- 옛 값 정리 — 이미 실려 있던 이웃 측점 4곳(260.0·800.0·900.0·980.0)에서 지웠음.
  **파생값이라 병합만으로는 안 지워짐**(merge 는 키를 얹기만 함).

⚠ **따로 드러난 것 — 관 3개(439.55·620.43·720.37)는 측점 행 자체가 없음.** 그 관들은 횡단
설계도 수량도 통째로 빠져 있음. 별건으로 조사할 것(계획서에 적음).

실측 결과 — 관 길이가 실린 측점 **6곳**(85.05→10.0 · 258.12→11.0 · 534.49→9.0 · 804.30→9.0 ·
901.24→10.0 · 984.18→10.0m). 나머지 셋이 위 별건임.

시험 7건(소유 측점 판정 한 건 추가) · 전체 507 passed · 17 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:36:23 +09:00
eomsangdonandClaude Opus 5 c4460892c0 feat(B08): 배수관 물량 — 정본 셋을 이어 인계에 실음
랩탑 창이 측점 design 에 pipe_length_m 을 넣어 줘서 마지막 조각이 채워짐.
B08 은 잇기만 하고 길이를 짓지 않음.

값이 어디서 오나
  관 자체(있나·어디·관경·관종) → pipe_points.json (레지스트리 managed_by)
  관 연장(m)                  → 측점 design.pipe_length_m
                                (B06 이 서버 Node 로 m 단위 올림까지 끝낸 값)
  관종 → 공종코드              → work_item_mapping 의 pipe.kind_codes
     파형강관 FP-12-11-03 · 흄관 FP-12-11-02 · VR관 FP-12-11-01

⚠ facility 가 pipe 인 점만 배관 — pipe_points.json 은 계곡 통과 시설 전부의
  정본이라 BOX암거·물넘이·세월교가 같은 파일에 있음. 관경 유무로 가르면
  관경 미지정 관을 놓침. 실측: 5601e828 11점 중 관 9 · 세월교 2

⚠ 관종 기본값(파형강관)은 2026-08-17 사용자 확정값이라 근거가 있으나
  조용히 쓰지 않고 「기본값으로 섰습니다」를 알림에 실음

⚠ 안 붙인 것 둘 — 터파기·되메우기(관 부설과 각각 오면 같은 굴착을 두 번 셈,
  B09 ㉡ 가드 자리) · 유출입부 기슭막이(관 옵션이 정본이고 구조물에서 빠졌음)

연장이 없는 관은 0 으로 때우지 않고 blocked_reason 과 함께 감
「횡단설계에서 [저장]을 한 번 누르면 그 측점의 관 길이가 남고 값이 섭니다」

시험 10건 — 세월교 걸러짐 · 관경 없어도 관임 · 연장 없으면 안 섬 ·
관종 셋이 갈림 · 기본값을 알림 · 모르는 관종은 못 고름 ·
⚠ 좁게: 옆 측점 길이를 물어 오지 않음 · 실제 자료 정본 대조

시험: 697 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:35:13 +09:00
eomsangdon b680141d2c Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 09:29:12 +09:00
eomsangdon 4169b2ce22 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-08 09:29:10 +09:00
eomsangdonandClaude Opus 5 562b43fd29 docs(B08): 옹벽 기초잡석이 원문에 없음을 자료에 못 박음
B09 가 「옹벽 묶음이 기초잡석 물량 없음으로 막혔다」고 알려 와 원문을 전수로
확인했음. 우리 표가 빠뜨린 것이 아니라 원문에 없었음.

울진 라이브러리 §7 「반중력식옹벽 H=2.0」 원문은
  콘크리트 1.35㎥(기초 0.75+벽체 0.60) + 버림 0.15㎥ · 유로폼 3.20㎡ ·
  기초 거푸집 0.6㎡ · 물빼기 파이프 Ø50 0.32m · 철근 D13 13.45㎏ + D16 30.42㎏
이 전부이고 기초잡석·터파기·되메우기·잔토가 다 없음.

기초잡석이 있는 시트는 §1 관보호공 날개벽뿐(T=0.2 · Ø800 A-TYPE 1.15㎥/개소).
다른 구조물 값을 옮겨 쓰지 않음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:29:07 +09:00
eomsangdonandClaude Opus 5 a6b5cb9548 chore(B09): 타설 겹침 제거 반영 확인 + 철근 「두 번」 검산
메인이 옹벽 묶음에 타설 조각이 이미 있는데 `_placing_rows` 가 그 콘크리트를
또 세던 것을 고침(`19d4009e`). 받아서 실물로 확인

- 인계에 **타설 줄이 없어짐** — 그 콘크리트는 묶음 조각이 셈
- 내역서 합계 3,136,861 → **2,149,464원** (메인 예고 약 2,149,464 와 일치).
  **결함이 아니라 겹친 것을 뺀 것**
- ⚠ 묶음이 미확보라 값이 안 걸렸을 뿐, **묶음이 서는 날 두 번이 됐을 자리**였음

철근 「철근공·보통인부가 두 번 들어 있다」는 메인 관찰 — **정상임을 검산**
- 품셈 12-3 표가 `구조별 | 가공 | 조립 | 계` 이고 진짜 자원 머리는 둘째 줄.
  가공·조립 두 몫을 각각 세는 것이 맞음
- 검산: 철근공 1.24 + 1.84 = **3.08** = 품셈 「계」 열 · 보통인부 0.45 + 0.75 =
  **1.20** = 「계」 열. **「계」 열은 안 더함**(더하면 두 배)

검증: pytest 212 통과. 스냅숏 전후 대조

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:26:36 +09:00
eomsangdonandClaude Opus 5 5f5e91d655 feat(B06): 배수관 연장(m)을 정본에 실음 — 수량이 관 공종을 셀 수 있게
B08 창 보고(2026-09-08): 관 11개가 `pipe_points.json` 에 있고 품셈 공종도 있는데 **연장(m)이
없어** 배수관 물량이 막혀 있었음. 관 길이는 횡단 기하가 **m 단위 올림까지** 끝낸 값
(`CulvertLayout.pipe.lengthM`)인데 **정본(`design`)에 안 실렸음**.

**옮기거나 새로 짜지 않았음** — 그 계산은 **이미 서버가 Node 로 그대로 실행**하고 있었음
(`B06_Section_Server_Calc_Node` ← `Server_Calc_Prebuild.recompute_server_side`, [저장]·[확정]).
구조물 폐회로 면적이 실리는 **그 다리에 값 한 줄만** 더 실었음 — 계산을 두 벌로 짜지 않음
(CLAUDE.md 5장). B08 창이 낸 갈래 둘(서버로 이관 / 저장 때 남기기)은 둘 다 불필요했음.

- `STRUCTURE_AREA_KEYS` → **`STRUCTURE_ROW_KEYS`** 로 이름을 고치고 `pipe_length_m` 추가.
  이름이 「면적」이라 길이를 담으면 거짓말이 됨.
- **면적을 못 내는 측점에서도 길이는 냄** — 설계선이 모자라 폐회로가 안 닫히는 자리에도
  관은 서 있고 수량은 그 길이가 필요함(`return pipeRow`).
- ⚠ **서버에 화이트리스트가 하나 더 있었음** — `_AREA_KEYS`(「Node 가 다른 키를 내도 안 흘림」).
  거기 안 넣으면 Node 가 값을 내도 **조용히 버려짐**. 넣고, **두 목록을 대조하는 시험**을 둠.
- 키 이름은 `pipe_length_m` 으로 **B08 창과 미리 못 박음** — 오늘 `back_len_cm` 이 엔진에서
  `stone_back_length_cm` 로 읽혀 저장값이 영영 안 닿던 사고가 있었음.

⚠ 값이 정본에 실리려면 그 프로젝트에서 **[저장] 한 번**이 필요하고, **번들 재빌드**
(`npm run build:server-calc`)도 필요함 — 번들은 git 밖이라 창마다 각자 돌려야 함.

시험 6건 — 패치로 받는지 · 음수 거부 · 면적과 **같은 목록**으로 실리는지 · 면적 실패 시에도
길이는 나오는지 · 이름을 두 번 적지 않는지 · **서버 화이트리스트와 TS 목록이 짝인지**.
전체 507 passed · 17 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:25:55 +09:00
eomsangdon 374f454a2c Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 09:24:57 +09:00
eomsangdonandClaude Opus 5 19d4009ec4 fix(B08): 옹벽 타설이 두 번 셀 자리 — 묶음 구조물은 타설 줄을 안 만듦
B09 가 「철근이 겹치나」를 물어 확인하다 드러남. 철근은 안 겹치는데
타설이 겹쳤음.

철근 — 안 겹침 (확인)
  자재로 가는 것은 재료(이형철근 D13·D16 kg)
  묶음 조각 FP-12-03 은 품 — 일위대가 실측 재료 0원 · 노무만
  ⇒ 품은 공종 줄, 재료는 자재 쪽. 타설(12-1-1)과 같은 구조

타설 — 겹쳤음 (고침)
  work_item_mapping 의 옹벽 composite 에 FP-12-01-01 조각이 이미 있는데
  _placing_rows 가 그 구조물의 콘크리트를 또 셌음
  ⇒ 묶음으로 서는 구조물은 건너뜀. 그 콘크리트는 묶음 조각이 셈
  ⚠ 묶음이 아직 미확보라 값이 안 걸렸을 뿐, 서는 날 두 번이 됨

시험 둘 — 묶음 구조물은 타설 줄 없음 · ⚠ 묶음 없는 종류는 그대로 섬
(좁게 고치는 짝. 묶음 하나 때문에 정상 줄까지 지우면 더 나쁨)
기존 타설 시험 셋은 표본을 옹벽 → 집수정으로 바꿈(옹벽이 이제 묶음이라).

시험: 685 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 09:24:14 +09:00
eomsangdonandClaude Opus 5 2f19ba3152 chore(B09): 마스터 갱신 반영 — 규준틀 두 줄에 금액이 붙음
메인이 원문 **표 아래 [주]③**(「목재의 손율은 1개소 사용당 50 %」)에서 밑수를
찾아 `FP-11-02`·`FP-11-03` 을 「1 개소」로 채움(`fc867d32`)

- 자원 축 다시 냄. 지문이 `3752af1e…` 로 따라 바뀜(낡음 감지 장치 동작)
- 기준 미상 68 → 66
- **비탈 규준틀 68,512.2원/개소 · 수평 규준틀 90,997.7원/개소** 로 섬
- 내역서(2f940d8a) 합계 **11,494 → 946,067원** — 규준틀 7개소·5개소가 실제로 실림
  · 3-1 비탈 규준틀 7 × 68,512.2 = 479,585
  · 3-2 수평 규준틀 5 × 90,997.7 = 454,988

⚠ 원문이 **표 위 본문이 아니라 표 아래 [주]** 에 있던 첫 사례 —
앞서 여덟 번은 다 표 위였음. 다음에 밑수를 찾을 땐 양쪽을 볼 것

검증: pytest 212 통과. 스냅숏 전후 대조로 준비공 6줄 신규 확인

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 08:38:34 +09:00
eomsangdon 6a410e757a Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 08:36:56 +09:00
eomsangdonandClaude Opus 5 fc867d3220 fix(B08): 규준틀 밑수를 표 아래 [주] 에서 읽음 — 금액이 못 붙던 자리
B09 제보. 규준틀 둘(FP-11-02·FP-11-03)이 in_bill=true 로 서는데
「밑수 미확보」라 금액이 안 붙고 있었음. 개소 수는 이미 닫혔는데
그 표의 밑수 자체가 비어 있던 것.

「원문에 있었음」 아홉 번째 — 이번엔 표 **아래** [주] 였음
  [주]③ 「목재의 손율은 **1개소 사용당** 50%로 한다」
  뜻으로도 개소가 맞음 — [주]② 가 「본 품은 …한 비탈규준틀의 제작·도색·
  가설·철거를 포함한 것」이라 규준틀 하나를 말하고, [주]① 이 「20m마다 설치」

⚠ 아주 좁게 잡음 — 「N개소 **사용** 당」 표기는 품셈 전문에 딱 두 곳뿐이고
  둘 다 규준틀임. 「개소당 평균면적」 같은 흔한 말은 「사용」이 없어 안 걸림
  (넓히면 조림 2장이 통째로 걸림 — 실측 19건).
  다음 표(|)나 다음 절(###)을 만나면 멈춤 — 남의 [주] 를 물어 오면 조용히 틀림.

효과: 밑수 확보 183 → 185, 미확보 137 → 135.

시험 넷 — 규준틀에서 읽는 것 · 흔한 말에 안 걸리는 것 · 다음 절로 안 넘어가는 것 ·
다시 만든 마스터에 실제로 실렸는지 정본 대조.

시험: 685 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 08:35:27 +09:00
eomsangdonandClaude Opus 5 fb86195576 feat(B09): 「여기서 세지 않는 줄」을 할 일과 갈라 냄 — 이중계상 자리
B08 이 준비공 여섯 줄을 인계에 실으면서(`93546b85`) 세 갈래가 섞여 옴

- ⚠ `blocked_kind` 가 **없는데 `in_bill=false`** 면 **막힌 줄이 아님** —
  「다른 표에서 이미 섬」(벌목·지장목제거)·「이 노선엔 없음」(사방 시설).
  그것을 「우리가 만들어야 하는 것」에 얹으면 사용자가 세우려 들고
  **그것이 곧 이중계상**(㉠~㉦ 규칙에 걸림)
- 화면 목록을 **셋으로** 가름 — 입력하면 풀리는 것(2) · 우리가 만들 것(10) ·
  **여기서 세지 않는 줄(2)**
- 검산 줄 비고에 B08 사유 문구를 그대로 실음
- 짝 시험 — 같은 `in_bill=false` 라도 사유가 다르면 갈래가 달라야 함

실측(5174, 2f940d8a): 세 무리가 각각 뜨고 검산줄 5개가 수량만 보임

검증: pytest 212 통과(신규 1), tsc 0건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 08:29:15 +09:00
eomsangdonandClaude Opus 5 deddee6989 feat(B08): 자재 원단위 밑수 미결을 화면에 드러냄
8-29 ㉮7(자재 원단위의 밑수 — 비탈면적인가 정면적인가)이 계획서에만
있고 화면에는 안 보였음. pending_choices 에 넣으니 구조물 원단위 탭에
바로 뜸 — 「무엇을 정해야 하나」와 「정하면 얼마나 달라지나(−4.4 %)」를 함께.

곁들여 확인 — 그 탭에 미확정 넷이 다 뜸:
  자재 원단위 밑수 · 흡출방지재·차수시트 · 목재틀흙막이 원단위 ·
  내역서 수량 표시 자릿수
계획서 8-25 가 뒤 둘을 「화면 」로 적어 두었는데 실제로는 보임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 08:27:48 +09:00
eomsangdon 878e5931bf Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 08:25:33 +09:00
eomsangdonandClaude Opus 5 93546b850a feat(B08): 준비공·사방공을 인계에 실음 — 못 내는 줄은 사유와 함께
보조 창 요청에서 출발했는데, 보다 보니 표가 통째로 안 가고 있었음.
표토제거(값 있음·FP-09-15)·규준틀(개소·FP-11-02)이 화면에는 서는데
인계에는 없었음.

⚠ 줄을 빼면 「빠졌다는 사실조차 안 보임」 — 받는 쪽에서 「내역서에 원래
없는 것」과 「우리가 아직 못 내는 것」이 구별되지 않음.

- 값이 서는 줄 → in_bill: True, 공종코드와 수량 그대로
- 못 내는 줄 → in_bill: False + blocked_kind + blocked_reason
  (금액은 안 붙되 「무엇이 채워지면 풀리는지」가 함께 감)
- ⚠ 「다른 표에서 이미 섬」(벌목)은 blocked_kind: None — 막힌 것이 아니라
  여기서 세면 안 되는 줄임. 막힘으로 보내면 「만들어야 할 것」으로 오해됨
- origin: "preparation" 로 갈래를 나눔

시험 넷 — 값 서는 줄 · 못 내는 줄의 사유 · 이중계상 방지 줄은 막힘 아님 ·
⚠ 표를 안 주면 빈 줄을 지어내지 않을 것(좁게 고치는 짝 시험).

시험: 681 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 08:23:03 +09:00
eomsangdon 428c2131cc Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 06:49:48 +09:00
eomsangdon dc38f872b5 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 06:49:32 +09:00
eomsangdon 362cd4d719 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-08 06:49:31 +09:00
eomsangdonandClaude Opus 5 6e114ac990 feat(dev): 확정 없이 다음으로 — 등록 + 좌측 단추 (2/2)
앞 커밋 618b6cf4 의 신규 파일을 등록하고 화면 단추를 붙임.
두 걸음 규칙대로 파일이 먼저 push 된 뒤 등록 줄이 감.

문은 앞뒤 둘
- 서버(정본): ENVIRONMENT 가 개발이 아니면 세 입구 모두 403.
  라우터는 다른 것과 같은 보호(로그인·회사·프로젝트 접근)를 받고
  그 위에 환경을 한 번 더 봄
- 화면(보조): import.meta.env.DEV 일 때만 단추가 그려짐

눌렀을 때 바뀌는 것 — project_workflow_stages.state 한 칸뿐.
계산은 안 돌고 정본 값도 안 만듦. 실측(b269ea34):
  우회 전 [] → 우회 [3 STALE · 4 NOT_STARTED · 5 NOT_STARTED] → 되돌림 → []
  이미 COMPLETE 인 0·1·2 는 안 건드림 (진짜 확정은 보존)

되돌리기 — 푼 단계의 옛 상태를 message 에 DEV_UNLOCK:<옛상태> 로 적어 두고
[원래대로 되돌리기]가 그대로 복원. 그 표시가 없으면 진짜 확정이라 안 만짐.

드러남 — 우회 중이면 좌측에 「지금 확정을 건너뛴 상태입니다 — 값이 비어
보이는 것은 정상입니다. 건너뛴 단계 (3, 4, 5)」가 뜸.

⚠ 화면에서 잡은 것 — 단추를 패널 아래에 두니 overflow:hidden 에 잘려
  화면 밖(y=772, 패널 740)이라 눌리지 않았음. 맨 위로 옮김(상태 경고이기도 함).

시험: 677 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관) · tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:48:19 +09:00
eomsangdonandClaude Opus 5 618b6cf4c5 feat(dev): 확정 없이 다음으로 — 개발 전용 잠금 해제 (신규 파일만, 1/2)
공용 파일 두 걸음 규칙 — 이 커밋은 신규 파일뿐이고 main.py 등록은 다음 커밋.
먼저 push 해야 다른 창이 등록 줄만 받고 파일이 없어 죽는 일이 없음.

까닭 — 단계마다 [확정]을 해야 다음 페이지가 열려서, 상세 설계를 확정하기
전에는 B08·B09 를 볼 수 없음. 오늘 화면 검증이 그 자리에서 두 번 막혔음.

⚠ 계산을 대신 돌리지 않음 — 잠금만 품
- 바꾸는 것은 project_workflow_stages.state 한 칸뿐
- 확정은 전 측점을 다시 계산해 정본에 쓰는 것이라, 흉내 내면
  「확정 안 했는데 확정된 값」이 생겨 막힌 것보다 나쁨
- 값이 없으면 B08 이 「미확보」로 뜨는 것이 정상

⚠ 문은 서버가 정본 — ENVIRONMENT 가 개발이 아니면 세 입구 모두 403
  (화면에서 단추를 숨겨도 API 가 열려 있으면 소용없음)

되돌리기 — 푼 단계의 이전 상태를 message 에 DEV_UNLOCK:<옛상태> 로 적어 두고
DELETE 가 그대로 복원. 그 표시가 없는 단계(진짜 확정)는 안 건드림.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:39:05 +09:00
eomsangdonandClaude Opus 5 60fbc51285 docs(OWNERS): desktop-sub 상태 대기로
조율 창 제어 종료에 따라 자기 줄만 갱신. 담당 폴더·브랜치·포트는 그대로.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:32:12 +09:00
eomsangdonandClaude Opus 5 1b5db4eb68 docs(B09): 「여기서 멈춤」 표시 + 장부 상태 대기로
조율 창 제어 종료에 따른 마무리

- 9-7 에 「여기서 멈춤」 — B09 완료 상태(표 다섯·화면 전수·가드 8·시험 211·
  tsc 0·700줄 0)와 **이어받을 사람이 볼 미처리 둘**
  · 값이 채워진 첫 인계 받기(볼 것 여섯은 바로 아래 표)
  · 타설 줄이 실린 자료로 65,826.4원/㎥ 이 금액에 걸리는지 확인
- 확정 답이 오면 손댈 자리 셋을 짚어 둠 — 제잡비 밑수 · 수량 자릿수 · 석재 할증
- `OWNERS.md` desktop-sub 줄을 「대기」로

브라우저 화면은 원래 탭(공사원가계산서)으로 되돌려 놓음.
워킹트리 깨끗 · 폴더 `D:\aislo-wt\sub` 확인

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:32:01 +09:00
eomsangdonandClaude Opus 5 556e71cffe docs(B09): 스냅숏 도구 + 「값이 채워진 첫 인계」에서 볼 것 여섯
대기 중 둘 처리

- `tmp/snapshot_b09.py before|after|diff` — 인계 응답과 **코드 시각**을 함께 뜸.
  `/api/health` 의 `code_mtime`·`stale` 을 같이 적어, 값이 움직였을 때
  「자료가 바뀐 것」과 「코드 버전이 바뀐 것」을 가름
  (성토 −16.81 % 가 그 자리였고 **절반이 코드 버전**이었음, 장부 규칙 7·8)
  ⚠ diff 는 **어느 줄이 얼마나** 바뀌었는지로 냄 — 합계 한 줄로 말하지 않음
- 9-7 에 「값이 채워진 첫 인계에서 볼 것」 여섯을 표로 적어 둠 —
  암 갈래별 단가 · 타설 줄(65,826.4원/㎥) · 반영률 곱한 수량 · 공급 구분 ·
  돌 규격/쌓기 방식 · 개소 구조물 단위(메인 `de31cd9c`)

내 검증 프로젝트는 `2f940d8a` (배정). 첫 스냅숏 떠 둠 — 공종 10 · 자재 0

검증: pytest 211 통과. 서버 `stale` 이던 것 재시작해 코드 시각 맞춤

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:31:06 +09:00
eomsangdonandClaude Opus 5 de31cd9c42 fix(B08): 개소 구조물이 인계에 연장(m)으로 나가던 것
㉕ 타설 줄 실증 중에 드러남. 관측 원단위가 「개소당」인 종류(집수정 셋)를
인계 줄이 늘 「m · 연장」으로 내보내고 있었음.

- 연장 2m 짜리 집수정이 「2.000 m」로 나가 값이 두 배로 실림
- 성분은 개소 기준으로 맞게 서는데 줄의 축만 어긋나 아무 시험도 안 잡았음
- billing_of() 로 관측표가 정한 단위·개수를 꺼내 전개 결과에 실음
- 인계 줄이 그 단위를 따름. 비어 있으면 종전대로 「m · 연장」

시험 셋 — 개소로 서는 것 · 연장이 길어도 한 개소 · ⚠ 정상 「m」 줄은
그대로일 것(좁게 고치는 짝 시험).

곁들여 타설 줄 실증: 옹벽 H=2.0(20m) + 집수정 Ø800 으로 돌리면
FP-12-01-01#철근구조물 32.840㎥ 가 인계에 섬(옹벽 27+3 · 집수정 2.84).

시험: 677 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:23:47 +09:00
eomsangdonandClaude Opus 5 759f8c59a4 fix(B09): 안내 줄 겹침 정리 — 같은 수가 두 번 뜨던 것
메인 창 지적(「안내를 더하는 것이 곧 다른 안내를 묻는 것」)을 내 화면에도 대어 봄

- 잘리는 줄은 없었음(본문 높이 = 스크롤 높이, 6줄 다 보임)
- 다만 **같은 수가 두 번** 떴음 — 「금액을 못 세운 줄 (16)」 뒤에 갈래별 머리글이
  또 붙어 「… (16)」이 이어짐. 머리글을 **한 번만** 달게 함 (6줄 → 5줄)
- 갈래가 하나뿐일 때만 「금액을 못 세운 줄」을 앞에 붙여 뜻이 온전하게 함
- 문구도 줄임 — 「0 으로 채우지 않고 그대로 보입니다」 → 「0 으로 안 채웁니다」 등

실측(5174): 안내 5줄, 잘림 없음. 바꾼 화면 상태는 원래 탭으로 되돌림

검증: pytest 211 통과, tsc 0건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:22:59 +09:00
eomsangdonandClaude Opus 5 2898e81a89 docs(B09): 9-7 에 화면 전수 결과·로그인 절차 보태 마감
- 화면 전수 표 다섯 탭 결과와 「화면에서만 잡힌 것」(자재 0건인데 빈 표 셋)
- 공용 브라우저 로그인 절차 — 사용자가 유지하는 자격 파일 + 페이지 오리진에서
  로그인 요청, 확인은 세션 200. ⚠ 파일이 내 폴더에 없으면 **그 자리에서 읽어 쓰고
  복사·기록하지 말 것**(자격을 옮기면 지우는 사람도 늘어남)
- 「튕기면 서버가 아니라 브라우저 쿠키를 의심할 것」

마감 상태: 표 다섯 화면으로 섬 · 가드 7 + 열 방향 검사 · pytest 211 ·
tsc 0건 · 700줄 초과 0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:19:45 +09:00
eomsangdonandClaude Opus 5 cdbc932717 feat(B09): 새 인계 칸 둘 수용 + 예시 서식 우회 제거(원천이 이김)
- 계약 시험이 **제 일을 했음** — 「인계가 보내는데 안 읽는 칸: spec_class,
  spec_class_basis」로 깨짐. 둘 다 읽고, 갈래 판정 근거(`spec_class_basis`)는
  **B08 문구 그대로** 줄 비고에 이음
- 예시 서식 거르기를 뺌 — B08 이 **마스터 원천에서** 거름(8건).
  두 곳에서 같은 것을 거르면 나중에 한쪽만 고쳐짐
- 짝 시험은 남기되 모양을 바꿈 — 인공 표 대신 **실물 마스터에서 그 8공종이
  자원으로 서지 않는지**. 원천이 바뀌면 이 시험이 알려 줌

검증: pytest 211 통과. 못 맞춘 자원 497(원천 필터 뒤 값과 일치)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:17:29 +09:00
eomsangdonandClaude Opus 5 f685845e5a fix(B08): 화면 통과에서 잡은 좌측 결함 둘
㉕-1 실사용자 화면 통과(로그인 뒤). 엔진 실행으로는 안 보이던 것들.

- 토량환산계수 세 줄이 서버 키로 뜨고 있었음 (soil · ripping_rock ·
  blasting_rock) → 토사 · 리핑암 · 발파암. 준비공의 soil_guard 와 같은 병.
  모르는 키는 지어내지 않고 그대로 보임.
- 타설 방식 안내가 겹쳐 경고를 밀어내고 있었음. 기본값일 때는 「기본값으로
  계산 중」 경고가 더 많은 것을 말하므로 내 줄을 안 실음. 문구도 줄임.
  실측: 줄이기 전에는 참고 단가·⚠ 두 줄이 패널 밖으로 잘려 안 보였음.

화면 확인(스크린샷·DOM): 탭 6장 다 서고 표가 따라옴. 표토 두께 칸은
입력 가능(회색은 「안 정함」 placeholder 색).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:16:42 +09:00
eomsangdon dfb92ca952 Merge remote-tracking branch 'origin/sub_laptop_1' into sub_desktop_1 2026-09-08 06:11:38 +09:00
eomsangdon 0d262573da Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-08 06:11:37 +09:00
eomsangdon 116bdaf829 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 06:11:35 +09:00
eomsangdonandClaude Opus 5 e714a80301 feat(B09): 자재가 없을 때 빈 표 셋 대신 한 줄 안내 + 검증 함정 기록
화면 전수(다섯 탭)에서 드러난 것

- 자재가 아예 없는 프로젝트에서 **빈 표 셋이 「(0) — 0」 으로 늘어서** 있었음.
  「이 프로젝트에는 자재 줄이 없습니다」 한 줄로 바꿈
- 계획서 「검증 함정 모음」에 셋 적음
  · 세션은 DB 라 재시작으로 안 지워지되 **튕기면 브라우저 쿠키를 의심할 것**
  · 화면이 막혀도 **엔진은 파이썬에서 직접 부를 수 있음** — 단
    `await init_db_pool()` 을 먼저 부를 것(안 부르면 **빈 응답이 「자료 없음」으로
    잘못 읽힘**), 한 이벤트 루프 안에서 끝낼 것
  · 되받기 전후로 **인계 응답을 파일로 떠 둘 것** — 「합계가 221원 줄었다」를
    짚으려면 어느 줄이 얼마나 바뀌었는지 대조할 자료가 있어야 함

화면 전수 결과 (프로젝트 b269ea34, 포트 5174)
  ⑤ 원가계산서 26줄 · [재계산] 동작
  ④ 예산내역서 16줄 · 금액줄 1 (측구터파기 92.45 × 5,288.6 = 488,926)
     비고에 「단산 1 참조」 · 안내 5 · 미확보 16 (두 갈래로 갈려 뜸)
  일위대가 203줄 · 안내 3
  ③ 단가산출서 1장 · 본표와 참조 코드
  자재대 — 이 프로젝트는 자재 0건이라 한 줄 안내(다른 프로젝트에선 미정 4건 확인)
  ⚠ 바꾼 화면 상태는 원래 탭으로 되돌려 놓음

검증: pytest 211 통과, tsc 0건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:11:22 +09:00
eomsangdonandClaude Opus 5 4f138794ab feat(B08): 콘크리트 타설 줄 인계 + 빈 단가산출서 서식 거르기
㉙ B09 2차 교차검토와 함께 나온 것.

콘크리트 타설 — placing_code() 가 시험에서만 불리던 자리를 실제로 이음
- build_handoff 가 구조물 종류별로 콘크리트 체적을 모아 타설 공종 줄을 냄
- 겹치지 않는 것을 확인함: 품셈 12-1-1 표가 직종·품만 주고 재료를 안 줌
  (서브 일위대가 B-FP-12-01-01#철근구조물 = 재료 0원 · 노무 65,826.48원)
  품은 이 줄, 재료는 자재 쪽으로 갈려 있음
- ⚠ 채움콘크리트는 뺌 — 돌쌓기 뒤채움이라 그 공종 품에 이미 들어 있을 수 있음
  (13-6 [주]① 이 큰돌쌓기는 품에 포함이라 못 박음, 13-4 는 확인 전)
- 방식을 안 정했으면 기본값으로 서되 placing_notes 로 알림
- 화면 문구를 「아직 반영 안 됨」에서 「방식에 따라 단가가 달라짐」으로 바꿈

빈 단가산출서 서식 — 품셈이 실어 둔 채워 넣으라고 둔 양식 8건
- 머리글 「단가산출서」가 정본 표지, 머리 두 줄 모양은 보조
- 값이 아예 없는 표라 공종으로 세우면 「값이 있는데 안 서는 자리」로 오해됨
- 실측: 견적 쪽 못 맞춘 자원 882 → 497, 밑수 미확보 160 → 137
- 자원 줄 418 · 단가 제목 330 은 그대로 — 정상 값은 안 지워짐

시험
- 서식 규칙 짝 시험 넷 (정상 표를 지우지 않는 것 · 다시 만든 마스터 대조 포함)
- 타설 줄 시험 넷 (방식 셋이 갈리는 것 · 콘크리트가 없으면 줄도 없는 것)
- 옛 시험 하나 정정 — 구조물 한 줄 규칙에 타설 줄이 걸리던 것

시험: 674 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관) · tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:08:31 +09:00
eomsangdon 3758761608 Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-08 05:54:39 +09:00
eomsangdon 87b5d1e783 Merge remote-tracking branch 'origin/sub_desktop_1' into sub_laptop_1 2026-09-08 05:54:31 +09:00
eomsangdon 736a5e7538 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 05:54:29 +09:00
eomsangdonandClaude Opus 5 b98dee3a06 fix(B09): 예시 서식 표 건너뜀 + 정규식 문자클래스 범위 오독 수정
⑤ 구간 표기 37줄을 살피다 둘이 나옴

- 그 줄들은 **살릴 값이 아니었음** — 품셈이 실어 둔 **단가산출서 예시(빈 서식)**
  안의 셀이었음(「ha당 참나무시들음병방제 단가산출서(예시)」).
  읽으려 들면 구간 라벨(「12~14㎝」)과 항목명이 자원 이름으로 오해됨
  ⇒ 머리글에 「단가산출서」·「예시」가 있으면 **표째 건너뜀**.
  못 맞춤 882 → 497, 성분 빠짐 84 → 78. 자원 축·일위대가·분포는 그대로
  (값이 없던 표라 당연함)
- ⚠ **진짜 결함** — `RANGE_DASHES` 를 정규식 문자클래스에 그대로 넣어
  `~-–` 이 **범위 연산자**로 읽히고 있었음. 「0.7㎥」·「15톤」까지 구간으로 잡힐
  자리였음. `re.escape` 한 `RANGE_DASH_CLASS` 를 두고 세 파일이 그것을 씀
  (「한 곳으로 모으라」는 지적이 없었으면 못 봤을 자리)
- 구간 셀에 **단위 꼬리**(「51~100m」)를 허용하되 **수~수+단위 전체가 맞을 때만** —
  단위가 붙었다고 다 구간은 아님(「굴착기 0.7㎥」는 규격)

계획서 — 석재 할증률 물음을 「몇 %인가」가 아니라
「붙일까 말까, 붙인다면 근거를 어디서」로 고침(메인 지적)

검증: pytest 211 통과(신규 2 — 예시 서식 건너뜀 + 정상 표는 안 걸림)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:54:18 +09:00
eomsangdon 5f965f2498 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 05:53:37 +09:00
eomsangdonandClaude Opus 5 8ccd577827 fix(B08): 돌쌓기 면적이 기울기 몫을 두 번 먹던 것 수정
㉘ 자기 감사 후속. 실무 시트의 「돌쌓기 = 정면적 × 1.04」에서 그 1.04 가
곧 기울기 몫임(1:0.3 → √(1+0.3²) = 1.0440). 시트가 반올림해 적은 것을
별도 계수로 오해해 hypot 위에 또 곱하고 있었음.

- 돌쌓기·큰돌쌓기 모두 기울기 몫을 한 번만 곱함
- 상수는 지우지 않고 `sheet_check_factor_at_0_3` 으로 성격을 바꿈 —
  1:0.3 에서 시트값과 맞는지 대조하는 검산 참고값
- 하드코딩 금지 이유를 주석에 남김: 큰돌쌓기는 「1:0.3 이상」이라
  기울기가 바뀌면 1.04 는 틀린 값이 되지만 hypot 은 따라감

값 변화 (실노선):
  고임돌 -3.85 % · 야면석 -3.85 % · 물구멍 -3.85 %
  막자갈 +3.20 % (입적에서 빼는 몫이 줄어 늘어남 — 식대로임)

시험
- 옛 시험이 덧곱을 계약으로 못 박고 있어 정정
- 거울 시험 추가: 기울기 0.3·0.5·1.0 에서 정면적 × hypot 과 일치
- 시트 대조 추가: 정면적 18.87 → 돌쌓기 19.62, 오차 0.5 % 안
- NULLABLE_SETTING_KEYS 계약 시험 추가 (홑 숫자 칸만 좁게 봄)

곁들여 타설 방식 안내 한 줄 — 고른 값이 아직 금액에 안 닿는다는 사실을
화면에 적음. 인계에 타설 공종 줄을 세울지는 이중계상 확인 뒤 결정.

시험: 666 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관) · tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:51:55 +09:00
eomsangdonandClaude Opus 5 faa113106b docs(B09): 9-7 마감 기록 — 표 다섯·확정 대기·막힌 것의 뿌리 셋
메인 창이 8장에서 한 것과 같은 모양으로 9장을 정리

- 완료 기록 — 표 다섯이 화면으로 섰음(⑤·③·④·일위대가·자재대).
  자원 축 418줄 · 일위대가 203 · 가드 7 + 열 방향 검사 · 시험 209
- 확정 대기를 **㉮ 지금 답할 수 있는 것 / ㉯ 더 알아봐야 하는 것**으로 가르고
  항목마다 세 칸(지금 답할 수 있나 · 무엇이 채워지면 풀리나 · 어디까지 찾아봤나)
- 「낼 수 있는 것 / 막힌 것」 표 — **막힌 것의 뿌리는 셋뿐**
  (사급 자재 단가 · 밑수 미확보 · 사용자 입력). 셋 다 우리가 만들 수 없는 것
- 데이터 파일 지문표 + 「지문은 마스터 파일 자체의 것이어야 함」 경위
- 「일하는 법」 — 품셈 표 일곱 모양, 「숫자가 늘어난 것은 좋아진 신호가 아님」,
  「크기가 말이 되나」가 네 번 다 잡은 것(셋이 최소값), 「있다와 돈다는 다름」,
  「사고가 난 자리에는 반드시 짝 시험」, 계약은 양쪽에서 마주 걸 것,
  `tmp/tests` 가 git 밖이라 교차검토로 시험 유무를 못 판정하는 한계,
  폴더 튐 네 번과 「쓰는 쪽에 경로 검사」

검증: pytest 209 · tsc 0건 · 700줄 초과 0 · ruff 22파일 정렬됨.
화면 전수는 공용 브라우저 로그아웃 상태라 로그인 뒤로 남김

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:46:32 +09:00
eomsangdonandClaude Opus 5 3e729439cb fix(B08): 자기 감사 — 안 부르던 검사 넷 연결 + 표토 두께 입력 칸
㉘ 자기 감사. B09 에 겨눈 네 축을 내 것에 그대로 겨눔.

안 부르던 검사 — 시험에서만 불리고 있던 것들을 실제 경로에 이음
- verify_no_code_on_materials · verify_bill_flags → build_handoff 결과에 실음
- check_against_plan → 운반 표 옆에 haul_check 로 실음 (계획이 있을 때만)
- 바로 위 주석이 「검사는 실제로 부른다」였는데 형제 둘이 놀고 있었음

표토 두께 — 서버·엔진은 받고 있었는데 화면에 넣을 칸이 없었음
- optionalNumberField 신설 (빈 값 = 「안 정함」, 0 과 구별)
- null 을 저장에서 버리던 것 수정 (NULLABLE_SETTING_KEYS) —
  한 번 넣으면 「안 정함」으로 못 되돌리던 자리

짝 시험 — 규칙은 있는데 시험이 없던 둘
- REFERENCE_MARKS (41건을 지웠던 그 규칙): 기준표는 잡고 「단 위」는
  안 잡는 것을 양쪽으로 박음
- attachments_of: 칸이 있을 때만 줄이 서고 부모와 id 가 갈리는 것

시험: 662 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관) · tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:46:12 +09:00
eomsangdonandClaude Opus 5 1959aec6ed test(B09): 시험 0 이던 가드 둘 채움 — 조종원 나눗수·뭉친 표 판정
메인 창이 「가드에 짝 시험이 붙어 있나」를 물었고, 세어 보니 둘이 0 이었음
(`tmp/tests` 가 git 밖이라 그쪽에서 확인할 길이 없었음)

- `check_operator_hours_basis` — 8시간 나눗수는 통과, 5.09시간으로 줄이면 걸림
  (나눗수를 줄이는 것이 곧 효율을 사용료에 넣는 것, 품셈 8-1-6)
- `match_packed_rows` — 뭉치지 않은 표는 **가로채지 않고**, 뭉친 표는 읽음
  (그 문이 넓었을 때 자원 축이 304 → 244 줄로 떨어졌음)

검증: pytest 209 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:41:25 +09:00
eomsangdonandClaude Opus 5 52d76a9286 fix(B09): 제잡비 밑수 = 직접노무비 + 안 불리던 가드 잇기 + 700줄 분리
메인 창이 B09 를 읽기 전용으로 교차검토해 낸 지적을 처리

제잡비 밑수 — **사람 품(직접노무비)만**으로 바꿈. 기계 안의 조종원 노임은 안 셈
- 근거 셋을 주석에 인용: 산림품셈 13-6-2 [주]③ 「노무비의 합계액」 ·
  건설품셈 제8장 「잡재료 등 손료 : **직접노무비**에 …」 ·
  같은 장에서 기계를 넣을 때는 「노무비, 기계손료 및 운전경비의 합」이라 **따로 적음**
- 뜻으로도 그쪽 — 제잡비는 **본 자원에 안 선 잔 기계 손료**를 사람 품에 비례해
  얹는 자리인데 그 표엔 굴착기가 이미 본 자원으로 서 있음
- 찰쌓기 60~80 기준 ㎡당 2,565.90 → **2,098.46** (제잡비 붙는 17줄 전부 걸림)
- ⚠ 잠정 — 사용자 확정 대기. 조종원 포함이면 약 +22 %

안 불리던 가드 둘 (「있다」와 「돈다」는 다름)
- ㉥ 물빼기 파이프 — **조판에서 실제로 부름**. 지금은 늘 아랫단이라 안 걸리되
  설계 조건이 실리는 날 그 값만 바꾸면 바로 걸림
- ㉢ 배합 분해 — 부를 자리가 아직 없음. **그 사실과 부를 위치를 코드에 적음**

700줄 제한 (CLAUDE.md 4장) — 셋을 나눔
- `_UnitPrice_View`(화면용 조회) · `_ResourceAxis_Sources`(자료 적재·셀 파싱) ·
  `_BillOfQuantities_Rows`(줄 만들기). 가르는 금을 각 파일 머리말에 적음
- 부르는 쪽이 어디서 오는지 신경 쓰지 않게 재수출

물결표 목록을 `RANGE_DASHES` 한 곳으로 모음 — 네 파일에 따로 적혀 서로 달랐음
(지금 물리는 것은 없었으나 같은 목록이 네 벌이면 언젠가 하나만 고쳐짐)

검증: pytest 206 통과, 700줄 초과 0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:39:47 +09:00
eomsangdon f1d86e2525 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 05:19:00 +09:00
eomsangdonandClaude Opus 5 47fa317b26 fix(B09): 관급 자재를 「사급」이라 적던 문구 갈라 냄
메인 창이 인계를 실제로 먹여 잡음 — 물구멍·야면석이 `owner_supplied` 인데
「사급 자재 단가 미확보」로 뜨고 있었음. `unknown` 만 가르고 나머지를 한 줄로
떨어뜨린 탓

- 관급 — 「관급 자재 단가 미확보 — 나라장터 목록에 이 품목이 없습니다.
  관급자재대(총원가 밖 별도 표기)로 갑니다」
- 사급 — 종전 문구(물가지 미결 No.18, 6번 슬롯 수동 입력 대기)
- `missing` 에 `supply_type` 을 실어 화면이 갈래로 묶을 수 있게 함
- 갈래마다 **가는 자리도 원천도 다름** — 관급은 총원가 밖·나라장터,
  사급은 도급 재료비·물가지

검증: pytest 206 통과(신규 1)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:15:35 +09:00
eomsangdonandClaude Opus 5 5755b1b413 fix(B08): 실사용자 통과에서 드러난 화면 결함 셋 수정
㉕ 통과(엔진 실행)에서 잡힌 것.

- 준비공 사방공 줄에 개발자 키(`soil_guard`)가 그대로 뜨던 자리 수정.
  레지스트리 이름을 받아 쓰고, 키는 `type_id` 칸으로만 오감.
  문구표에 사방 시설 이름 넷 추가(레지스트리 값 그대로 옮긴 것).
- 좌측 「토사」 비율 칸 제거 — 넣어도 버려지던 죽은 칸.
  토사 물량은 토적표 값이 그대로 서고, 비율은 암 총량을 갈래로 나눌 때만 쓰임.
- 안분 안내 문구를 「암 갈래 입력 합 …」으로 명시.
  칸에 100 을 채운 사용자에게 「입력 합 60 %」로 뜨던 자리.
- 항목 라벨을 「암 갈래 구성비(%) — 암 총량 기준」으로 바꿈.

시험: 655 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 이 변경과 무관).
사방공 이름 시험 2건 추가, 키를 화면 이름으로 못 박던 옛 시험 1건 정정.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:13:35 +09:00
eomsangdonandClaude Opus 5 e250d2f716 feat(B09): 자재대 표 신설 — 관급·사급·미정 셋으로 갈라 냄
PLAN 8-7 「자재대·관급자재대(금액)는 B09」 — B08 자재총괄이 낸 수량·할증에
단가를 붙여 금액을 내는 자리. 수량은 B08 것이 정본이라 다시 세지 않음

- **관급은 총원가 밖 별도 표기**(⑤ 관급자재대와 같은 값), 사급은 도급 재료비
- ⚠ `unknown`(관급·사급 미정)은 **어느 합계에도 안 넣음** — 넣으면 총액이 틀리고
  어느 쪽으로 넣었는지 나중에 못 가림. 지금 실물 4건이 전부 이 상태
- 단가가 없으면 **금액을 안 만들고** 사유를 남김(사급 물가지 미결 No.18)
- 할증률이 없으면 「할증 전 수량」임을 표에 적음 — 여기서 또 곱하지 않음(㉠)
- 관급자재대 합계는 **천원 올림**(단수처리 규칙)
- 화면 탭 신설 — 세 무리를 각각 표로 보이고 합계·사유를 함께 냄

검증: pytest 205 통과(신규 5 — 합계 분리·미정 제외·단가 없음·할증 깃발·천원 올림),
tsc 0건. 화면 확인은 공용 브라우저 세션 만료로 다음 차례에

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 05:02:26 +09:00
eomsangdonandClaude Opus 5 5bd129bae4 fix(B08): 갈래 키를 조립하지 않고 원본값으로 보냄 + 특수문자 깃발
⚠ 계약 변경(2026-09-07 3자). 품셈 원문이 규격 표기를 한 종류로 안 씀 —
**물결표만 셋**(`∼` U+223C 662회 · `~` U+FF5E 459회 · `~` U+007E 2회),
곱셈표도 둘(`×` 97회 · `x` 4회). 같은 절 안에서도 갈림(13-06-01·02 는 `∼`,
13-06-03 은 `~`). **두 창이 각자 키를 조립하면 글자 하나로 영영 안 맞음.**

- `#55cm이하`·`#직경60㎝이상∼80㎝미만` 같은 키를 **우리가 만들지 않음**.
  `variant_axis`(어느 축) + `variant_value`(저장 원본값 그대로)만 보내고,
  원문을 읽는 쪽이 표기를 흡수함. 「…㎝ 이하」 구간 나누기도 그쪽 몫 —
  그 구간이 품셈 표의 구조이기 때문.
- ⚠ **메/찰은 예외** — 키 표기가 아니라 **공종 자체가 갈리는 의미 판정**이라
  코드가 `FP-13-06-01`/`-02` 로 통째로 달라짐. 그것은 우리 몫으로 유지.
- 계약 시험에 못 박음 — 코드에 `#`·`㎝`·`cm` 가 들어가면 깨짐. 원본값을
  가공해도 깨짐(물결표 종류를 바꾸거나 단위를 붙이면 안 됨).
- `data_masonry` 의 갈래표는 지우지 않고 **참고**로 남김 — 서브 판정과
  어긋나면 그것이 곧 신호.

마스터에 `special_glyphs` 깃발 — 규격 표기에 쓰인 특수문자 종류. 값은 안 고치고
표시만 함. 물결표가 두 종류 섞인 표가 실제로 5개 있는 것을 시험으로 못 박음
(계약 근거가 사라지면 깨짐).

검증 — 전체 653 passed, tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:57:54 +09:00
eomsangdonandClaude Opus 5 db0b38d826 feat(B09): 갈래 키를 이쪽에서 만듦 — 물결표 통일 + 구간 판정
두 창 합의 — B08 은 **의미**만 보내고(`work_item_code` + 저장 제원 원본값),
품셈 원문 표기를 키로 옮기는 것은 **원문을 읽는 이쪽 몫**

- 품셈이 물결표를 두 종류로 섞어 씀(`∼` U+223C 26건 · `~` U+FF5E 8건) —
  같은 뜻인데 키가 두 벌이었음. 키에서만 한 종류로 모으고 **원문 문구는 이름에 보존**
- ⚠ 규칙은 둘뿐 — **내부 공백 제거 + 물결표 통일**. 키에 쓰인 글자를 세어
  그 밖에는 소수점·괄호뿐임을 확인(하이픈·곱셈표 없음)
- 「60~80」은 「직경60㎝이상~80㎝미만」 **안에 없어**(사이에 「㎝이상」이 낌)
  글자 포함으로는 못 맞춤 → **수의 짝**으로 견줌
- 저장 제원이 한 값으로 오는 경우(뒷길이 45㎝)는 **그 값을 담는 가장 좁은 구간**을
  고름 — 45 → `#55cm이하`, 25 → `#35cm이하`
- ⚠ 담을 갈래가 없으면 `None` — **가까운 것을 임의로 고르지 않음**(95 → 없음)
- 조판이 `variant_axis`·`variant_value` 를 받아 코드를 고르고, 못 고르면
  종전대로 후보를 보임

검증: pytest 198 통과(신규 5 — 짝 시험 포함)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:56:51 +09:00
eomsangdonandClaude Opus 5 089f80b550 feat(B08): 성토고를 사면표에 실어 수평 규준틀 개소를 셈
㉔. 확정 7(준비공 3줄)의 마지막 하나가 닫힘.

- 사면길이 유도가 이미 성토 사면을 재고 있어 **성토 조각의 수직 낙차 합**이 곧
  성토고임. `StationSlope.fill_height_m` 으로 실어 사면표까지 옮김.
  ⚠ 좌우가 다르면 **큰 쪽**을 씀 — 「중심점 성토고 5m 이상」 판정은 가장 높은
  쪽이 기준이고 양쪽을 더하면 실제보다 두 배가 됨.
- 수평 규준틀은 **성토고 5m 이상 측점마다 한 개소**(11-3 [주]①).
  ⚠ 비탈규준틀(「10m 이상 20m마다」)과 **기준이 다름** — 이쪽은 지점 조건이라
  간격이 없음. 두 기준을 같은 식으로 쓰면 조용히 틀림.
- 성토고 칸이 아예 없으면 0 으로 때우지 않고 미확보로 둠. 성토고가 다 낮으면
  0 개소가 **값**임 — 「못 셈」과 다름. 셋을 시험으로 갈라 못 박음.

실노선 확인 — 비탈 규준틀 31개소(617.475m ÷ 20 + 1) · 수평 규준틀 39개소.

검증 — 준비공 18건 통과, 전체 647 passed, tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:52:43 +09:00
eomsangdonandClaude Opus 5 d8cbd48baf feat(B09): ③ 단가산출서(D층) 신설 — 내역 줄에서 검산 경로가 이어짐
실무 내역서는 줄마다 비고에 「단산 46 참조」를 적고 그 산출서를 펴서 검산함(8-13).
STC 실측도 `D` 가 `B`(일위대가)를 참조하는 한 층 위였음(9-3)

- `PriceBook` 의 「제목 + 상세」 한 쌍에 `kind=PRICE_BASIS` 만 얹음 —
  표를 세 벌 만들지 않음. 화면도 일위대가 탭과 **같은 2단 모양**
- 번호는 **코드에 안 박음** — 실무 참조번호는 그 내역서 안의 차례라
  프로젝트마다 다름. 코드는 공종을 가리키고 번호는 조판할 때 매김
- 같은 일위대가가 두 줄에 쓰이면 **산출서는 한 장**, 두 줄이 같은 번호를 가리킴
- 내역 줄 비고에 「단산 N 참조」를 달음
- 지금은 일위대가를 그대로 한 줄로 참조 — 할증·기타 비용이 붙을 자리를 미리 열어 둠
- `GET …/estimation/price-basis/{code}` 로 본표 조회

화면 실측: 내역 줄 「2-2-1 측구터파기 488,926 / 단산 1 참조」,
  산출서 탭에 1장(토사 FP-09-12-01 ㎥ 5,288.6), 눌러 본표·참조 코드 확인

검증: pytest 195 통과, tsc 0건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:51:34 +09:00
eomsangdonandClaude Opus 5 d0978d2422 feat(B08): 큰돌쌓기 메/찰 자동 판정 + 전면 기울기 근거
랩탑이 B06 에서 상세 제원을 받게 하고 `bond` 칸을 더해 준 것을 받음.

- `bond`(메쌓기/찰쌓기) → 품셈 `FP-13-06-01`/`FP-13-06-02` 자동 판정.
  미입력이면 `blocked_kind: input_missing` 과 사람 말 사유로 드러냄 —
  랩탑이 「빈 값을 미확보로 계속 드러내 달라」고 한 그대로. 첫 항목을 슬쩍
  고르면 근거 없는 값이 수량·단가로 흘러감.
- `pending_user` 에서 큰돌쌓기를 뺌 — 칸이 생겨 자동으로 갈림.

전면 기울기 1:0.3 의 근거를 찾아 데이터·주석에 적음. 지어낸 값이 아니라
**교본 7-3 돌흙막이 기준**임 — 「돌 찰쌓기 3.0m 이하 1:0.3 / 돌 메쌓기 2.0m 이하
1:0.3 / 큰돌쌓기 1:0.3 이상(전도 방지)」(지식DB 돌쌓기.md §1).
⚠ 큰돌쌓기는 「1:0.3 **이상**」이라 더 완만하게 잡을 수 있음 — 칸을 만든다면
그 범위를 보여야 한다는 것도 데이터에 적음.

검증 — 전체 642 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:43:58 +09:00
eomsangdon 3a3374d0ff Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 04:43:41 +09:00
eomsangdonandClaude Opus 5 2e9e0d1f4a feat(B09): 막힘 사유를 갈래별로 화면에 가름
B08 이 `blocked_reason`(사용자 말 문구) + `blocked_kind` 를 실어 보내기 시작함

- 문구는 **B08 것을 그대로** 씀 — 두 벌로 짜면 한쪽만 고쳐지는 자리가 됨
- 화면 미확보 목록을 두 갈래로 가름
  · 「입력하면 풀리는 것 — 설계 화면에서 값을 고르면 금액이 섭니다」(`input_missing`)
  · 「우리가 만들어야 하는 것 — 원단위·전개식이 아직 없습니다」
- 한 목록에 섞이면 사용자가 「후보를 고르면 되나」로 잘못 읽음(돌쌓기가 그 자리였음)
- ⚠ 빈 값은 오류가 아니라 **「아직 안 고른 상태」** — 랩탑이 「— 선택 —」 빈 칸으로
  열어 둔 것과 짝임(첫 항목을 슬쩍 고르면 근거 없는 값이 단가로 흘러감)

계약 시험의 측점 까닭 보강 — 「지금은 안 씀 — 구간별 산출근거를 낼 때 쓸 것」

검증: pytest 195 통과, tsc 0건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:43:28 +09:00
eomsangdon 56ab6d9598 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 04:42:17 +09:00
eomsangdon b3f1816a49 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-08 04:42:16 +09:00
eomsangdonandClaude Opus 5 ae431bb17d feat(B08): 규준틀 개소 · 목재틀 갈래 · 표토 두께 칸
㉓. ㉒ 에서 원문을 찾아 닫힌 셋을 구현.

- **비탈 규준틀 개소** — 품셈 11-2 [주]① 「비탈길이 10m 이상 20m마다 설치」.
  사면표의 측점별 사면길이·거리로 셈. ⚠ 「10m 이상」은 비탈길이 조건이고
  「20m마다」는 노선 거리 간격이라 둘을 안 섞음. 재료량은 [주]④ 「설계수량」이라
  개소만 내고 재료는 미확보로 둠.
- **수평 규준틀**은 11-3 [주]① 이 「중심점 성토고 5m 이상」인데 성토고가 사면표에
  없어 0 으로 때우지 않고 미확보로 둠.
- **목재틀흙막이 갈래** — 품셈 13-13-1 [주]③ 이 재료 구성으로 가름.
  임도 흙막이는 「보통구조 상(통나무 기슭막이)」로 잠정 판정. ⚠ 잠정임을 근거
  문구에 드러내고 「상등구조를 쓰면 8.760 → 16.975 인/㎥ 로 약 1.9배」를 함께 적음.
  갈래표는 데이터 파일(`resources/data_timber/`)이라 줄만 더하면 늘어남.
  ⚠ 밑수 1㎥ 는 구조물 체적이 아니라 **목재 채적**임을 데이터에 명시 — 그래야
  「1㎥에 건축목공 17인」이 말이 되는지 판단됨.
- **표토제거 두께** — 9-15 [주]② 가 「T : 표토두께(m)」로 공식의 입력 변수로 둠.
  품셈이 정하는 값이 아니므로 설계 입력 칸(`quantity.topsoil_thickness_m`)으로 냄.
  ⚠ 기본값 없음 · 미입력이면 물량 안 냄(0 으로 때우지 않음). 대상 면적은 참고로 보임.

검증 — 준비공 16건 통과, 전체 642 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:42:04 +09:00
eomsangdonandClaude Opus 5 032ea53ede feat(B06): 구조물 상세 제원을 B06 폼에서 받게 — 수량이 품셈 갈래를 고를 수 있게 됨
데스크탑 창 보고(2026-09-08): 실무 프로젝트 구조물의 **상세 옵션이 통째로 비어 있음** —
`back_len_cm`(뒷길이) · `stone_cm`(돌규격) · `form`(옹벽 형식). 그래서 B08 수량이 품셈 갈래를
못 골라 **금액이 0 으로 남음**(13-4 돌쌓기 뒷길이 · 13-6 큰돌쌓기 직경 · 12-3 옹벽 형식).

**까닭 — 아무도 안 받고 있었음.** 레지스트리가 그 칸들을 `phase: "detail"` 로 두고
「B05 는 유무·종류·위치만, **상세 치수는 B06/B07 에서**」(2026-08-17 사용자 확정)로 갈라 놨는데,
**그 화면이 아직 안 그리고 있었음**. 폼은 한 벌인데 필터(`isB05Option`)가 늘 상세를 숨겼음.

고침 — 폼이 `includeDetail` 을 받아 **B06 에서만** 상세 칸까지 그림(B05 는 종전 그대로).
새 결정이 아니라 **2026-08-17 확정을 이제 구현한 것**임.

⚠ **기본값 없는 필수 항목은 빈 칸(「— 선택 —」)으로 연다** — 첫 항목을 슬쩍 고르면 근거 없는
값(뒷길이 25㎝ 같은)이 수량·단가로 흘러감. 설계자가 고르게 둠.

**큰돌쌓기에 「쌓기 방식」(메쌓기·찰쌓기) 옵션 신설** — 품셈이 13-6-1 메 / 13-6-2 찰로 갈라
두는데 구분 칸이 없었음(돌쌓기는 이미 타입이 갈려 있음). 기본값 없음·필수·상세.

실화면 확인(용화 5601e828 · B06) — C군 → 큰돌쌓기 고르면 **돌규격·쌓기 방식** 칸이 빈 채로 뜸.
돌규격 `60~80` · 쌓기 방식 `찰쌓기` 로 한 건 넣고 [저장] → 정본 `structures.json` 에
`{"stone_cm": "60~80", "bond": "찰쌓기", …}` 로 남는 것 확인. 시험분은 지우고 저장해
**원래 7건**으로 되돌림.

시험 5건 — B06 만 상세를 받는지 · B05 는 종전대로인지 · 기본값 없는 필수는 빈 칸인지 ·
큰돌쌓기 메/찰 구분 · **규격 축이 타입마다 다른지**(큰돌쌓기=직경, 돌쌓기=뒷길이 — 데스크탑이
섞여 있던 결함을 잡은 자리). 전체 501 passed · 17 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:39:39 +09:00
eomsangdon af3b7c6d83 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 04:38:51 +09:00
eomsangdonandClaude Opus 5 fecda89e5f feat(B09): 인계 계약 시험 + ㉦ 큰돌쌓기 포함자재 가드 + 수량 2자리 표시
인계 계약 시험 (같은 병 재발 방지)
- **B08 이 보내는 칸을 내가 실제로 읽는지** 시험으로 마주 걺. 새 칸이 오면 깨짐.
  안 읽는 칸은 까닭을 함께 적게 함(측점·spec_detail 등)
- 값이 자료형까지 실제로 닿는지도 짝으로 봄 — 이름만 맞고 값이 안 실리면 같은 병

㉦ 가드 신설 (품셈 13-6 [주]① 「고임돌 및 채움 콘크리트 등은 품에 포함」)
- 큰돌쌓기 줄이 서 있는데 **그 구조물이 낳은** 고임돌·채움콘크리트가 자재로도 서면 멈춤
- ⚠ 13-6 한정 — 돌쌓기 13-4 의 고임돌은 정상이라 안 걸림. 짝 시험 2건
- 내 일위대가는 그 둘을 안 세우고 있음을 확인(품셈 표에 없음)

수량 표시
- 내역서 수량을 **소수 2자리**로 보임. 계산은 전정밀 그대로
- 「표시는 2자리, 계산은 전정밀 — 표시값끼리 곱하면 끝자리가 다릅니다」를 표 아래 상시 표시
  (실무 서식 자릿수는 기준 문서에 없어 2자리는 잠정)

검증: pytest 195 통과(신규 5), tsc 0건, 화면 실측 — 15,726.93 · 92.45 로 뜸

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:38:03 +09:00
eomsangdonandClaude Opus 5 4deaf0239c feat(B08): 인계 줄마다 막힌 까닭을 실음 + 계약 칸 대조 시험
서브 화면에 「왜 안 서는지」가 안 뜨던 줄 셋(돌쌓기·흙막이·큰돌쌓기)을 고침.

- `blocked_reason` · `blocked_kind` 를 줄마다 실음. 갈래는 셋 —
  `input_missing`(입력하면 풀림) / `unit_data_missing`(자료 없음) /
  `formula_missing`(산출식 없음). 받는 쪽이 「사용자가 입력하면 풀리는 것」과
  「우리가 만들어야 하는 것」을 화면에서 갈라야 함(8-27 표와 같은 축).
- 문구는 `B08_Quantity_Wording` 것을 그대로 씀 — 두 벌로 짜면 갈림.
- 토공·운반 줄은 막힐 자리가 없어도 **칸은 둠** — 계약이 한 모양이어야 함.

⚠ 계약 칸 대조 시험 추가(`test_b08_handoff_contract.py`).
오늘 이 병이 계약 양쪽에서 하나씩 났음 — 우리 쪽은 레지스트리 키와 엔진이 읽는
키가 달라 저장값이 안 닿았고, 받는 쪽은 우리가 보낸 `composite_parts`·
`quantity_gross` 를 안 읽고 버렸음. **「보내는 쪽은 보냈는데 받는 쪽이 안 읽는」
자리는 양쪽 다 조용함.** 키를 늘릴 때 계약에 안 적고 늘리면 받는 쪽이 영영 모름.
자재 줄에 공종코드가 없는 것도 계약으로 못 박음.

검증 — 전체 638 passed. 실물 프로젝트(`5601e828`)에서 네 줄 다 사유가 실림.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:35:31 +09:00
eomsangdonandClaude Opus 5 56e0351389 feat(B09): 인계 새 칸 7개 수용 + 묶음(옹벽) 줄 조립
B08 이 보내는 칸 중 **하나(`application_ratio_pct`)만 읽고 있었음** — 나머지는
조용히 버려지고 있었음. 메인이 「저장값이 안 닿아 늘 기본값으로 돌던」 결함을
잡은 것과 같은 자리라 대조해 발견

- 새로 읽는 칸: `quantity_gross` · `application_ratio_breakdown` ·
  `quantity_breakdown` · `composite_parts` · `composite_not_ready` ·
  `structure_kind`
- **묶음 줄 조립** — 품셈에 그 공종이 없는 것(옹벽)은 조각들의
  `단가 × 조각수량` 합이 곧 그 줄의 1단위 단가. 조각이 하나라도 비면
  **금액을 안 만들고** 사유를 그대로 적음
- `composite_not_ready` 만 온 줄도 묶음으로 봄 — 그러지 않으면 「코드 없음」으로
  뭉뚱그려져 무엇이 없는지 안 보임
- 구조물 줄은 사유를 갈라 적음 — 「구조물 전개식(원단위)이 없어 조각을 못 세웠습니다」

실측(구조물 시험 프로젝트 5601e828): 23줄 · 합계 540,244원
  돌쌓기(찰) 4줄이 「후보 3건」으로 뜸 — 뒷길이가 저장 안 돼 갈래를 못 고름.
    임의로 고르지 않고 후보를 보임
  옹벽은 「원단위 미확보 (retaining_wall H=2.5) — 표에 있는 규격: 반중력식 H=2.0」
    으로 사유째 뜸
  ㉥ 가드 오탐 없음(메인이 택한 아랫단 + 물구멍 자재 조합에서 안 걸림)

검증: pytest 190 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:31:11 +09:00
eomsangdonandClaude Opus 5 755e37ba55 feat(B08): 화면 사유 문구를 사용자 말로 — 키 이름 안 새게
㉑. `back_len_cm` 같은 개발자 키가 그대로 화면에 뜨던 자리를 모두 바꿈
(`B08_Quantity_Wording.py`). 반영률 라벨이 서버 키로 뜨던 그 자리와 같은 병.

- 「없다」만 말하지 않고 **어디서 채우면 값이 서는지**를 함께 적음.
  「돌 뒷길이(㎝)가 아직 입력되지 않았습니다 — 구조물 상세 입력에서 입력하면
  값이 섭니다」 · 「옹벽의 이 규격은 자료에 없습니다 — 자료에 있는 규격:
  옹벽 형식 반중력식 · 높이(m) 2.0」
- 규격도 사람 말로 — `form`·`height_m` 이 아니라 「옹벽 형식」·「높이(m)」.
- ⚠ 모르는 키는 지어내지 않고 그대로 보임. 잘못된 안내가 없는 안내보다 나쁨.
- 키가 새는지 검사를 둠(`test_b08_wording.py`) — 새 문구를 넣다 흘리면 깨짐.

검증 — 문구 9건 통과, 전체 633 passed. 실물 프로젝트(`5601e828`)로 문구 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:31:07 +09:00
eomsangdonandClaude Opus 5 b06ae123aa docs(B08): 옵션 키 어긋남 대조 — face_slope_ratio 는 칸 자체가 없음
`back_len_cm` 사고(레지스트리 키와 엔진이 읽는 키가 달라 저장값이 영영 안 닿던 것)
뒤 전수 훑기. B08 이 읽는 옵션 키 7개를 레지스트리와 대조한 결과 어긋남은 하나뿐이고
그것은 이름 어긋남이 아니라 **칸 자체가 없는 것**임(`face_slope_ratio` — 전면 기울기,
늘 기본 0.3 으로 돎). 「칸이 생기면 받는다」는 뜻으로 남겨 두고 코드에 그 사실을 적음.

대조를 시험으로 둠(`tmp/tests/test_b08_option_keys.py`) — 「이름으로 알아보는 코드는
정본과 대조하라」의 데이터 키 판. 칸이 생겼는데 목록만 남는 것도 잡음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:23:17 +09:00
eomsangdonandClaude Opus 5 c2a8f7cc41 feat(B09): 제잡비를 경비로 붙임 + ㉥ 물빼기 파이프 가드 + 성분 빠짐 차단 확대
제잡비 (품셈 13-6-1·13-7-1 [주]③, 원문은 메인이 찾아 줌)
- **노무비 합계 × 비율이 경비(J)로** 붙음. 재료·노무가 아님
- 기본은 **아랫단**(물빼기 파이프 미설치). 「상한」이라 산출근거에 그 사실을 적음
- `PriceDetail.percent_of_labor` 신설 — 밑수가 3분할 전체인 `percent_of_parent` 와
  다름. 비율 줄은 참조를 풀기 전에 처리(자기 자신을 가리켜 순환으로 잡혔음)
- 표시 합계에도 넣음 — 안 넣으면 화면 합계와 실제 단가가 어긋남

㉥ 가드 신설 — 제잡비 윗단(파이프 설치)을 쓰면서 물구멍을 자재로 또 실으면 멈춤
  (원문이 「상단에는 파이프 설치의 노무비·재료비를 포함한다」고 명시).
  짝 시험 4건 — 아랫단·파이프 없음·비슷한 이름은 안 걸림

성분 빠짐 차단 확대
- 시간·대수로 세는 줄(장비 몫)을 못 맞추면 그 공종 금액을 안 만듦.
  초본류 시비가 「트럭(2.5t) 2.6시간」을 빼고 33.1원/㎡ 로 서 있었음
- 자재 줄(kg·매)은 이미 알려진 미결이라 막지 않고 드러내기만 함
- 요약의 「의심 저가」에서 막아 둔 공종은 제외 — 새로 살펴야 할 것과 섞이지 않게

뭉친 표 두 곳 수정
- 칸 전체를 한 이름으로 먼저 시도 — 「굴착기 (무한궤도)」가 안 풀려 찰쌓기 13-6-2 의
  장비 몫이 빠지고 공종이 막혀 있었음
- ⚠ 라벨 줄 판정에 **순수 숫자 칸** 검사 추가 — 「자재 | 종자 | kg | 0.025」를
  라벨 줄로 오해해 씨앗뿜어붙이기 표를 가로챘음(회귀, 시험이 잡음)

결과: 자원 축 354 → 418, 일위대가 176 → 203, 막힌 공종 32
  찰쌓기 13-6-2 = 118,794.9원/㎡ (노무 + 굴착기 0.48h + 제잡비 3 %)

검증: pytest 190 통과(신규 6)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:22:56 +09:00
eomsangdonandClaude Opus 5 3741f8919f feat(B08): 큰돌쌓기(13-6) 전개식 + 돌쌓기 뒷길이 갈래 자동 판정
⑲. 앞서 막아 둔 큰돌쌓기를 13-6 축으로 세움.

- `boulder_masonry` 전개식 — 규격 축은 **직경**(40~60·60~80·80~100㎝)이고
  저장 제원 `stone_cm` 이 품셈 축과 글자까지 같아 그대로 씀.
  치수로 낼 수 있는 것(면적·터파기·되메우기·잔토)만 내고 나머지는 사유와 함께 알림.
  - 재료(큰돌)는 13-6 [주]⑦ 「재료량은 설계수량을 적용한다」라 원단위가 없음.
  - 고임돌·채움콘크리트는 [주]① 「품에 포함」 — 따로 세우면 이중계상.
  - 뒤채움(조약돌)은 [주]⑧ 로 13-3 적용. 두께는 13-4-3 [주]⑨ 준용인데
    직고별 **범위값**이라 한 값으로 못 정함 — 미확보.
  - 터파기 폭 규정이 품셈에 없어 돌쌓기 방식을 준용하고 그 사실을 근거에 적음.
- ⚠ 메/찰 구분 칸이 저장 제원에 없어 13-6-1/13-6-2 를 못 고름. 레지스트리는
  다른 창 소관이라 손대지 않고 `pending_user` 로 드러냄.

⚠ 함께 발견 — 엔진이 읽는 옵션 키가 레지스트리와 달랐음.
레지스트리는 `back_len_cm` 인데 엔진은 `stone_back_length_cm` 을 읽고 있어
**저장값이 영영 안 닿고 늘 기본 45㎝ 로 돌았음**. 뒷길이를 75 로 골라도 45 계수가
붙던 자리이고, 값이 나오므로 아무 시험도 안 잡았음. 두 이름 다 읽게 하고,
표에 없는 값(25·30·75)은 **덮는 위 칸**으로 접되 그 사실을 적음.

돌쌓기 뒷길이 갈래를 자동 판정해 코드에 붙임(`FP-13-04-05#55cm이하`).
「…㎝ 이하」 구간이므로 **저장값 이상인 첫 경계**를 고름. 근거 문구를 함께 실음.
갈래표는 `resources/data_masonry/` 에 데이터로 둠.

검증 — 전체 621 passed, tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 04:18:25 +09:00
eomsangdon 0e223ba0b1 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-08 04:12:58 +09:00
eomsangdon eadbc35647 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-08 04:12:56 +09:00
eomsangdon 37cb478b64 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 04:12:46 +09:00
eomsangdonandClaude Opus 5 15bdf6bd64 feat(B09): 뭉친 이름+갈래 열 표 읽기 — 돌쌓기 계열 성립
메인이 `reference` 오판 41건을 되살려(bc4b6837) 돌쌓기(장비)가 마스터에
들어왔으나, 그 표는 **이름도 값도 뭉쳐 오고 열이 규격 갈래**인 또 다른 모양이라
여전히 안 섰음 (「석공 보통인부 | 0.09 0.05 | 0.08 0.04 | 0.07 0.03」)

- 뭉친 이름·값을 갈래 열에 짝지어 읽음. 개수가 어긋나면 표째 버림
- 「굴착기+부착용 집게」는 **두 기종 조합**으로 보아 같은 시간을 둘 다 붙임
  (TODO(미결) 해석 잠정 — 사용자 확인 대기)
- 규격이 **옆 칸**에 있고 갈래(무한궤도)가 안 적힌 기종은 규격 일치 + 무한궤도
  우선으로 잠정 채택. 카탈로그 규격이 범위(0.6∼0.8)면 그 안에 드는지로 판정
- ⚠ 「1.04(1.17)」 괄호값은 **조건 시공 시 대안값**임이 원문에서 확인됨
  (13-6-1 [주]② 흡출방지재 시공 시 ( ) 값). 기본은 괄호 밖, 대안값은
  `alternative_amount` 로 남겨 화면에 「시공 시 다름」으로 보이게 함
- 라벨 줄 판정을 **모든 칸**으로 — 첫 칸만 보면 기초잡석 12-25 를 라벨 줄로
  오해해 표째 가로챘음(그 탓에 기초잡석이 한때 다시 막혔음)
- 갈래 키를 **공백 없는 것**으로 통일(`#보통`), 원문 문구는 이름에 보존
  (두 창 합의)

결과: 자원 축 259 → 354, 일위대가 145 → 176
  돌쌓기(찰) 35cm이하 58,166.7 / 55cm이하 52,938.7 / 75cm이하 46,892.7 원/㎡
  메쌓기·찰쌓기·붙이기 계열도 직경 3갈래로 섬

검증: pytest 184 통과(신규 1). 화면 실측 — 지금 인계 자료에 구조물 줄이 없어
표에는 아직 안 뜸(B08 이 갈래를 골라 보내면 그대로 금액이 섬)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 02:04:42 +09:00
eomsangdonandClaude Opus 5 4e1241a2cd fix(B08): 큰돌쌓기를 돌쌓기 식으로 돌리던 것 + 미확정 조건 셋 화면 노출
⚠ 마감 중 발견 — `boulder_masonry`(큰돌쌓기)를 `stone_masonry(dry)` 로 전개하고
있었음. 큰돌쌓기는 품셈 13-6, 돌쌓기는 13-4 로 **규격 축이 다름**: 돌쌓기는
뒷길이(35·45·55·60㎝), 큰돌쌓기는 직경(40~60·60~80·80~100㎝). 직경 60~80㎝
짜리가 「뒷길이 45㎝」 계수로 돌아 고임돌 0.15·야면석 0.88 이 붙고 있었음.
값이 나오기는 해서 어떤 시험도 안 잡던 자리 — 전개식이 설 때까지 미확보로
드러내고(`EXPANDER_WITHHELD`) 왜 안 두는지 사람이 읽게 적음.

⑱ 화면에 안 보이던 미확정 조건 셋을 구조물 원단위 탭에 띄움. 「무엇을 정해야
하는지」만으로는 부족하고 「정하면 얼마나 달라지는지」까지 적음.
- 흡출방지재·차수시트 — 보통인부 1.04 → 1.17 인/10㎡(약 +12.5 %), 근거
  13-6·13-7 [주]②. ⚠ 큰돌쌓기·큰돌붙이기에만 걸리고 돌쌓기(13-4)에는 괄호 값
  자체가 없다는 범위도 함께.
- 목재틀흙막이 원단위 — 1㎥당 건축목공 16.975인, 각재·판재가 없어 모자란 값.
- 내역서 수량 표시 자릿수 — `단수처리_규칙.md` 에 금액 자리만 있음.

검증 — 전체 612 passed, tsc 오류 0. 화면에서 셋 다 뜨는 것 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 02:02:52 +09:00
eomsangdonandClaude Opus 5 a4c7c8c8cd feat(B08): 이름 표기 갈림·공식 기호 깃발 + 물빼기 파이프 이중계상 규칙
서브 창 제보 교차 확인. 값을 바꾸지 않고 깃발만 실음.

- `spaced_names` 20건 — 「굴 삭 기 (무한궤도)」처럼 자간 공백이 든 자원 이름.
  같은 표 묶음 안에서도 표기가 갈림(13-6-1 은 공백, 바로 옆 13-6-2 는 없음).
  받는 쪽이 이름으로 찾으면 그 줄이 통째로 빠짐.
  ⚠ 여기서 이름을 고치지 않음 — 정규화는 값을 살리지만 잘못된 줄도 함께 살림
  (서브 실례: 정규화 직후 버킷계수 K 를 소요량으로 읽어 사용료가 이중).
  ⚠ 처음 101건으로 넓게 잡혔던 것을 **단위 칸 + 수치가 함께 있는 자원 줄**로
  좁혀 20건. 짝 시험(「단 위」·「모 래」는 안 걸림)을 같이 둠.
- `formula_rows` 24건 — 값 자리에 K·f·E·Cm 가 온 줄. 자원으로 세면 이중계상.

㉥ 물빼기 파이프 이중계상 — 품셈 13-6-2·13-7-2 [주]③ 이 제잡비 **윗단** 값에
「물빼기 파이프 설치에 관계되는 노무비, 재료비를 포함한다」고 함. 우리 원단위가
물구멍을 자재로 내므로 그 쪽과 겹침.
- **우리 선택: 물구멍을 자재로 세우고 제잡비는 아랫단(미설치)** 을 씀. 까닭은
  자재 줄로 세우면 규격·수량이 눈에 보이고 되짚을 수 있기 때문. 데이터와 코드
  양쪽에 적음.
- ⚠ 지금 쓰는 13-4 계열에는 **제잡비 행 자체가 없어 겹치지 않음**(전수 확인).
  이 규칙은 13-6·13-7 을 쓰게 될 때 걸림.

관측값이 원문과 맞는 첫 사례 기록 — 채움콘크리트 0.2㎥/㎡ 가 품셈 13-6-2 [주]⑩
「뒤채움콘크리트량은 0.2㎥ 기준」과 일치.

갈래 키 정규화 — `code` 는 내부 공백만 제거(`FP-12-03#보통`), 원문 문구는
`kind_label` 로 함께 실음. 다른 글자는 안 건드림(괄호·기호 유지 짝 시험).

검증 — 품셈 43건 통과, 전체 611 passed, tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:55:04 +09:00
eomsangdon 17e24e2789 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 01:49:44 +09:00
eomsangdonandClaude Opus 5 0728e4bae7 fix(B09): 기종 이름 표기 맞춤 + 공식 계수 줄을 자원으로 안 셈
석재 할증률을 찾으러 12·13장 본문을 훑다 발견한 둘

- 품셈은 같은 기종을 「굴 삭 기」·「굴착기」·「유압식백호우」로 섞어 적음.
  메쌓기 13-6-1 의 「굴 삭 기 (무한궤도)」가 안 붙어 **장비 몫이 통째로
  빠져 있었음**. 이름 안 공백 제거 + 기종 별칭만 맞춤(규격은 안 건드림)
- ⚠ 그 정규화가 새 오독을 열었음 — 측구터파기의 「유압식백호우 … | k | 0.9」
  줄이 자원으로 잡혀 **버킷계수 0.9 를 소요량으로** 읽었음(사용료 0.9시간분
  이중). 공식 기호(K·f·E)를 단 줄은 자원으로 세지 않음 — 그 줄은 시공능력
  공식 쪽에서 씀

결과: 자원 축 256 → 259, 일위대가 145 유지, 최소 233.4 / 최대 5,030,954.6

석재 할증률은 **품셈에 없음을 확인** — 1-3-1 재료 할증률표에 야면석·막자갈·
고임돌이 없고, 13-4~13-7 돌쌓기 본문 [주]에도 할증 문구 없음. 사용자 확정 대기

검증: pytest 183 통과(신규 2 — 공식 줄 제외 + 공백 이름 매칭 짝 시험)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:48:47 +09:00
eomsangdonandClaude Opus 5 bc4b6837e6 fix(B08): 「단 위」 표지가 공종 41건을 참조로 버리던 것 + 작업조 표 깃발
⚠ 서브 창 제보(유로폼 12-38-3 이 참조로 찍혀 읽히지도 않던 것)를 파고 들어가
더 넓은 결함을 찾음. `REFERENCE_MARKS` 의 `"단 위"` 하나로 **41건이 참조로
버려지고 있었고**, 그 안에 **돌쌓기(장비) 13-4-5·13-4-2**(우리 매핑이 실제로
쓰는 코드) · 12-2 표면 마무리 · 9-19-1 면고르기 · 9-20 뿌리다듬기 ·
13-8 막돌쌓기 · 11-2 규준틀 같은 명백한 공종이 섞여 있었음.

- 원래 겨냥은 품셈 1-2-2 「단위 표준」 표였는데 **1장은 chapter 규칙이 이미
  거르므로** 그 표지는 넓기만 했음. 「단위 열이 있다」는 소요량표의 흔한
  모양이지 참조표의 표지가 아님. 제거.
- reference 98 → 58, requirement 246 → 287. 돌쌓기(장비) 둘이 공종으로 섬.

작업조 표 깃발(`crew_table`) 16건 — 「형틀목공 4인 + 보통인부 1인 / 시공량 35㎡」
의 「4」는 소요량이 아니라 **인원**이라 그냥 읽으면 35배 부픔. 값을 바꾸지 않고
표시만 함(`expression_cells`·`partial_ratio` 와 같은 취급). 평떼 시비도 잡힘 —
서브가 실물에서 트럭 한 대를 빠뜨린 그 표.

유로폼 조각에 `incomplete_note` — 물량은 섰으나 **자재 몫(패널 0.89매/10㎡ ·
부자재 24/52/79 % · 소모자재 5 %)이 자재 단가가 없어 안 붙음**. 「못 채움」과
달리 값이 있으므로 「부분」으로 갈라 보임 — 부분 성공이 완전 실패보다 위험함.

검증 — 품셈 39건 통과, 전체 605 passed, tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:47:33 +09:00
eomsangdonandClaude Opus 5 a8f0248bf7 feat(B09): 작업조+시공량 표 읽기 — 유로폼 노무 성립
품셈에는 소요량 대신 **「작업조 몇 인이 하루 몇 ㎡」**로 주는 표가 11건 있음.
1단위당 품 = 인원 ÷ 시공량. 행-자원으로 읽으면 **인원 4를 소요량 4로** 오해해
35배 부풀어, 형태 판정보다 **먼저** 가름
(유로폼 12-38-3 은 마스터에서 `reference` 로 찍혀 형태 필터에 버려지고 있었음)

- 유로폼 설치·해체: 형틀목공 4 ÷ 35 = 0.1143인/㎡, 보통인부 1 ÷ 35 = 0.0286인/㎡
  → 유형별 갈래로 세움 (복잡 51,009.1 · 보통 36,435.0 · 간단 31,880.7 원/㎡)
- 유형은 우리가 안 고름 — 품셈 12-38-3 [유형] 이 「보통: 측구·수로·옹벽」으로
  정해 두었으므로 갈래로 세우고 고르는 것은 B08 (철근 12-3 [주]① 과 같은 모양)
- ⚠ 못 푼 작업조 줄이 있으면 **표째 버림** — 평떼 시비에서 「트럭 2.5ton 1대」가
  조용히 빠지고 노무만으로 28.6원/㎡ 이 서 있었음(분포 최소값이 233 → 28.5 로
  떨어진 것으로 발견)
- 이름 안에 공백이 든 줄(「비 계 공」)은 한 이름으로 먼저 시도 — 공백을 구분자로만
  보면 그런 표가 통째로 버려짐

결과: 자원 축 246 → 256, 일위대가 139 → 145, 최소값 233.4 유지

검증: pytest 181 통과(신규 6 — 손계산 대조 + 오탐 짝 시험 포함)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:42:20 +09:00
eomsangdonandClaude Opus 5 8b0f7e8baf feat(B08): 유로폼 갈래 원문 자동 판정 + 못 채운 조각 사유를 구조로
「원문이 이미 정해 둠」 **네 번째** — 품셈 12-38-3 [주]④ 「보통: 측구, 수로,
옹벽, 일반적인 벽체, 박스」. 앞선 셋(거푸집 사용횟수 1-7-1 · 철근 갈래
12-3 [주]① · 밑수는 표 위 본문)과 같은 자리라 사람이 고를 칸을 안 만듦.
옹벽 → `FP-12-38#보통`, 근거 문구를 조각에 함께 실음.

- ⚠ 12-38-1 「사용횟수」는 1-7-1 과 **다른 자리**임. 그쪽은 유로폼(강재)의
  잔존율(12회 25% · 25회 10%)로 임대료·손료 산정용이고, 1-7-1 은 소모성
  거푸집 전용 횟수(옹벽 3회)임. 이름이 같아 하나로 이으면 조용히 틀림 —
  데이터에 그 구분을 적고 시험으로 못 박음.
- 유로폼 자재는 12-38-2 가 10㎡당 패널 0.89매로 냄. B08 은 **면적(㎡)** 으로
  보내고 매수 환산은 밑수를 아는 B09 가 함 — B08 이 환산하면 밑수가 두 벌.

`composite_not_ready` 를 `{code, reason}` 구조로 바꿈. 받는 쪽이 「단가 없음」과
「물량 없음」을 갈라야 함(기초잡석이 후자).

원단위 자체가 없으면 조각을 늘어놓지 않고 한 줄로 말함 — 같은 사유가 조각마다
다섯 번 반복되면 진짜 사유가 묻힘(화면에서 실제로 그렇게 보였음).

시험 추가 — 같은 공종코드가 지반만 달리해 여러 줄 오는 표본(도자 토사·리핑암).
실물에서 잡힌 모양이라 고정 자료에도 넣음.

검증 — 인계 59건 통과, 전체 600 passed, tsc 오류 0.
화면 그리기는 실제 모듈을 그대로 돌려 확인(사용자 프로젝트·DB 무접촉).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:41:47 +09:00
eomsangdonandClaude Opus 5 573d7bbae2 feat(B09): 예산내역서 화면 탭 신설 + ITEM NO. 겹침 수정
화면
- 「설계내역서」 탭을 켜고 [B08 수량 불러오기] 로 표를 세움.
  계층은 들여쓰기, 머리글 줄은 굵게
- 못 세운 줄은 0 으로 채우지 않고 **사유째** 표와 목록에 보임
  (「일위대가 없음」·「하위 공종에 있음(후보 2건)」·「밑수를 못 찾은 표」)
- 검산용 줄(보정량계·무대)은 수량만 보이고 금액칸이 빔
- ⚠ 「사급 자재 단가가 없어 자재비가 빠져 있음 — 지금 합계는 모자란 값」을
  합계 옆에 상시 표시

ITEM NO. 겹침 수정
- 같은 공종코드가 지반만 달리해 두 번 오면 번호가 겹치고 있었음
  (실물: 도자운반 토사/리핑암이 둘 다 `3-1`). 잎 줄은 번호를 재사용하지 않음

실측(공용 브라우저 5174, 운반이 실린 실물 자료):
  16줄 · 겹친 번호 0 · 합계 488,926원
  운반 4줄이 3-1~3-4 로 서고 무대 2줄은 검산줄로 빠짐 (㉡ 가 실물에서 돎)

검증: pytest 175 통과(신규 1), tsc 0건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:36:11 +09:00
eomsangdonandClaude Opus 5 a9749a7e91 feat(B09): ④ 예산내역서 엔드포인트 추가
`GET /api/projects/{id}/estimation/bill` — B08 인계를 서버 안에서 그대로
받아 계층을 세우고 단가를 붙여 돌려줌

- 수량은 B08 것이 정본, 여기서 다시 세지 않음 (CLAUDE.md 5장)
- 단가 없음·밑수 미확보·공종코드 없음은 0 으로 안 때우고 `missing` 으로 냄
- 이중계상 감시에 걸리면 표를 그리지 않고 409 로 멈춤

실측(프로젝트 5cff3920, 공용 브라우저 5174):
  14줄(세부 7·머리 7) · 검산줄 1 · 자재 4 · 본체합계 478,680원
  측구터파기 90.51㎥ × 5,288.6 = 478,680, 나머지 6줄은 사유째 미확보로 뜸

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:28:56 +09:00
eomsangdonandClaude Opus 5 92e0ddca01 feat(B08): 묶음 조각 부위별 수량 + 철근 갈래 원문 자동 판정
옹벽 네 조각이 B09 에 다 서서 이제 물량을 실어 보냄.

- `composite_parts` 에 조각마다 수량·단위·근거(`basis_kind`)를 실음. 코드만
  보내면 받는 쪽이 상세 줄을 못 세움. 조각은 **원단위 성분 이름으로** 찾고
  못 찾으면 0 이 아니라 사유와 함께 `not_ready` 로 남김.
- ⚠ 철근은 ㎏ → ton 환산. 단가가 원/ton 인데 원단위는 ㎏ 이라 안 맞추면
  1000배 틀림. D13 13.45 + D16 30.42 = 43.87㎏/m × 10m = 0.4387ton.
- 철근 갈래는 **품셈 12-3 [주]① 원문**이 정함 — 「간단: 중력식 옹벽 /
  보통: 반중력식 옹벽 / 복잡: 부벽식 옹벽」. 거푸집 사용횟수(1-7-1)와 같은
  자리라 사람이 고를 칸을 만들지 않음. 캔틸레버식은 예시에 없어 「보통」으로
  때우지 않고 미확보로 드러냄.
- 전개 결과에 저장 제원(`options`)을 실어 뒤 단계가 형식을 읽게 함. 치수를
  다시 쓰라는 뜻이 아니라 읽으라는 것 — 치수 정본은 여전히 하나.
- 화면(구조물 원단위 탭)에 묶음 조각과 갈래를 보임.

⚠ 옛 시험 하나를 계약 변경에 맞춰 옮김 — `composite_not_ready` 가
「일위대가가 아직 안 선 공종」에서 「물량을 못 채운 조각」으로 뜻이 바뀜.

검증 — 인계 54건 통과, 전체 594 passed, tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:27:47 +09:00
eomsangdonandClaude Opus 5 01578eaed6 feat(B08): 타설 방식 단가 차이 표시 + 값 자리 식 깃발
타설 방식 — 「무엇을 정해야 하는지」만으로는 부족하고 「정하면 얼마나 달라지는지」가
보여야 사용자가 판단함. 세 방식 단가 차이가 6배라 조용히 기본값을 쓰면 총액이 갈림.
- 방식 이름을 늘 값 옆에 보임(「적용 중: 레디믹스트 (기본값 — 확인 필요)」).
  코드(12-01-01)만으로는 무엇을 쓰는지 모름.
- 참고 단가 세 줄을 안내로 띄움 — 레디믹스트 65,826 · 기계비빔 163,508 ·
  인력비빔 408,327 원/㎥. ⚠ **표시 전용**이며 B08 의 어떤 계산에도 안 들어감
  (금액은 B09 몫). 출처·산출일을 데이터에 함께 적음.

품셈 마스터 — 값 자리에 식이 적힌 칸에 깃발(`expression_cells`).
`0.2 × 30%`(기초잡석 소할)처럼 계산이 그대로 적힌 칸은 값이 숫자로 안 읽혀
그 성분이 조용히 빠짐. 형태 판정은 통과하고 배분율 딱지도 없어 아무 검사에도
안 걸리던 자리(서브 창이 실물에서 부딪힘 — 기초잡석이 부설다짐 0.6인만으로 섬).
⚠ 식을 계산하지 않고 드러내기만 함 — 뜻을 잘못 읽으면 조용히 틀림.
31표에서 잡힘(9-12·9-13 터파기 계열 · 12-24 뒷채움 · 12-25 기초잡석 등).

검증 — 품셈 34건 통과, 전체 588 passed, tsc 오류 0.
화면에서 방식 이름·기본값 표시·단가 세 줄 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:21:44 +09:00
eomsangdonandClaude Opus 5 50f3ab70d3 fix(B09): 「값 못 읽음」 오탐 좁히고 「값 × 비율%」 표기 읽기
- ⚠ 앞 커밋의 「값 못 읽은 줄」 판정이 **머리 줄까지 잡고 있었음** —
  「특별인부 | 벌목부 | 보통인부」처럼 자원 이름만 나열된 줄. 28 공종 중
  대부분이 오탐이었음. **숫자가 있는데** 못 읽은 줄만 성분 빠짐으로 봄 (28 → 12)
- 「0.2 × 30%」꼴을 0.06 으로 읽음. ⚠ 딱지의 30 % 를 **또 곱하지 않음** —
  두 번 곱하면 0.018. 그 밖의 식(「5인/km」)은 기준이 달라 안 읽고 막은 채 둠

기초잡석 12-25 성립 (121,661.1원/㎥)
  소할 할석공 0.06 + 부설다짐 보통인부 0.6 + 적사 굴착기 0.04535hr
  (Q = 3600 ÷ 22 × 0.7 × 0.55 × 1.0 × 0.35 = 22.05㎥/hr)
  운반(덤프)은 품셈이 10장에서 따로 내므로 여기 안 붙음

⇒ 옹벽 묶음 4조각이 모두 섬 — 타설 65,826.4 · 거푸집 81,321.9 ·
   철근 1,032,497.4 · 기초잡석 121,661.1

검증: pytest 174 통과(신규 4 — 오탐 짝 시험 포함:
머리 줄은 안 걸리고, 숫자 있는 미해독 줄은 걸림)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:20:48 +09:00
eomsangdonandClaude Opus 5 7591670e93 fix(B09): 값 못 읽은 자원 줄이 있는 공종은 금액을 막음
기초잡석 12-25 를 붙이다 발견 — `소할(30%) | 할석공(인) | 0.2 × 30%` 를
못 읽어 **부설다짐 0.6인만으로 107,145원**이 서고 있었음. 값이 그럴듯해
어떤 검사에도 안 걸림(「부분 성공이 가장 위험」)

- 이름 칸 판정을 **낱말 목록이 아니라 「풀리는지」**로 바꿈 — 첫 칸이 공정 이름인
  표(기초잡석)가 딱지 목록에 없어 통째로 못 맞추고 있었음. 자원 축 231 → 243
- **자원은 알아봤는데 값을 못 읽은 줄**이 있으면 그 공종을 `partial_items` 로
  표시하고 일위대가 금액을 만들지 않음. 그런 공종이 **28건** 있었고,
  그중 23건이 조용히 성분 빠진 단가로 서 있었음
- 못 읽은 줄은 `unmatched` 에 사유째 남김

검증: pytest 172 통과(신규 2 — 표시되는지 + 실제로 막는지 짝)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:14:47 +09:00
eomsangdonandClaude Opus 5 24fc6ec090 feat(B08): 화면에 안 보이던 확정 대기 셋을 드러냄
목록에만 있고 화면에 없으면 사용자는 그것이 잠정인 줄도 모름. 셋 다
「지금 어떤 값으로 돌고 있는지 + 왜 잠정인지」를 함께 보임.

- 콘크리트 타설 방식을 좌측 패널 칸으로 냄. ⚠ 금액에 바로 걸리는 값이라
  기본값으로 돌고 있으면 「기본값 「레디믹스트」로 계산 중 — 아직 안 정한 값」
  안내를 띄움. 설정 기본을 None 으로 바꿔 「안 정함」과 「일부러 레디믹스트를
  고른 것」을 가름 — 값을 미리 넣으면 그 구별이 사라짐. 되돌리기도 됨.
- 물구멍 근거에 잠정값을 적음 — 「관 Ø 미정(법 3~6㎝ / 실무 Ø50) ·
  간격 2.0㎡당 1개소(법 2~3㎡당 1개소 이상)」. 「미확정」만으로는 무엇을
  정해야 하는지 모름.
- 준비공의 벌목 줄에 공종 미확정 사유와 후보를 함께 적음(수확베기·단목베기·
  위험목 베기 중 어느 것인지 원본이 말하지 않음).

검증 — 전체 580 passed, tsc 오류 0. 화면에서 셋 다 뜨는 것과 타설 방식
저장·되돌리기까지 확인 후 검증으로 바꾼 값은 원래대로 복원.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:14:34 +09:00
eomsangdonandClaude Opus 5 d78f59ba5c feat(B09): 밑수 반영 재생성 + 밑수 미확보 줄 차단 + ㉡ 무대 가드 가동
B08 이 밑수(「10㎡당」 같은 기준 수량)를 본문에서 찾아 181표를 채움.
그 위에서 자원 축·일위대가를 다시 냄

- **떼채취 평떼 1,032,408 → 10,324원/㎡** — 100 배 부풀어 있던 것이 교정됨.
  「100매당일 것」이라던 의심이 실제로 `100㎡당`이었음
- 기준 미상 122 → 40, 100만원 초과 10 → 2 건. 중앙값 73,989 → 48,961
- 파생물 지문이 새 마스터(`fe454c56…`)로 따라 바뀜 — 낡음 감지 장치가
  처음으로 실제 갱신에서 동작 확인됨

밑수 미확보 표(`basis_missing` 144건 → 26 공종)
- 1 단위당으로 단정하면 곱셈이 10배·100배 틀리므로 **곱하지 않음**.
  내역서에 「밑수를 못 찾은 표입니다 — 곱하지 않았습니다. 원문 4-4. 가지정리」로 뜸

㉡ 무대 가드 가동 (B08 이 운반을 실물로 내기 시작)
- 인계에서 `haul_distance_m`·`haul_equipment` 를 받아 조판에서 실제로 호출
- 무대(20 m 이내)는 줄로 서되 금액 없음. 운반토량 합이 총 절취량을 넘는지도 검사
- 짝 시험 — 무대에 단가가 붙으면 멈추고, **암 운반·덤프 줄은 안 걸림**

검증: pytest 170 통과(신규 3)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:11:32 +09:00
eomsangdon 52467b1c87 Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-08 01:06:08 +09:00
eomsangdon 612909ac39 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-08 01:06:07 +09:00
eomsangdonandClaude Opus 5 06329e2101 fix(B09): 표 자리 맞춤을 개수 대조로 바꿔 조용한 오독 3종 차단
2단 표를 열자마자 과잉 매칭이 드러남 — 자원 축이 208 → 373 으로 뛰고
최대 단가가 2,436만원이 됨. 「한 칸 밀림」 가정이 표마다 안 맞았음

- **좌우 두 판 + 2단 머리** — 뿌리돌림 05-2 는 `근원직경 | 수량 | 근원직경 |
  수량`이라 오른쪽 판의 **직경 100 이 「특별인부 100인」**으로 읽혔음.
  첫 줄 머리가 되풀이되면 2단이라도 버림
- **라벨 칸 수가 표마다 다름** — 드론방제 08-6-2 는 라벨이 둘(`구 분 | 항 목`).
  고정 보정 대신 **숫자 칸을 순서대로** 맞추고 개수가 다르면 그 행을 버림.
  또 머리 줄 이름을 하나라도 못 풀면 표째 버림 — 넷 중 둘만 풀린 채
  숫자 둘이 **엉뚱한 직종**에 붙고 있었음
- **차원이 하나 더 있는 표** — 뭉기기 13-12-1 은 행이 공정, 숫자 칸이 토질
  3갈래인데 자원 열은 하나뿐이라 **첫 토질 값만 조용히** 서고 있었음.
  숫자 칸이 자원 열보다 많으면 버림

결과: 자원 축 231줄(갈래 88) · 일위대가 133 · 최대 5,030,954.6
(품셈대로인 값). 철근 12-3 과 콘크리트 타설 12-1 3갈래는 그대로 섬

검증: pytest 167 통과(신규 2). 갈래 표 5건 손대조 — 전부 표와 일치

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:05:11 +09:00
eomsangdonandClaude Opus 5 c12b1027f7 feat(B08): 콘크리트 타설 갈래 — 철근 유무 자동 판정
조율 창 판정 반영. 타설 공종은 타설 방식 × 구조물 종류로 갈림.

- **구조물 종류는 자동 판정** — 원단위 성분에 철근이 있으면 철근구조물.
  사람이 고르는 값이 아님. 옹벽 관측 원단위의 D13·D16 에서 그대로 나옴.
- **타설 방식은 설계 판단**이라 프로젝트 설정(`quantity.concrete_placing_method`)이
  고르고 기본은 레디믹스트(FP-12-01-01). 잠정이며 사용자 확정 대기.
  모르는 방식이면 기본으로 떨어지되 그 사실을 알림.
- 옹벽 묶음의 타설 코드를 `FP-12-01-01#철근구조물` 로 좁힘.
- ⚠ 묶음 넷 중 `FP-12-03`·`FP-12-25` 는 B09 일위대가가 아직 안 섬.
  `composite_not_ready` 로 함께 알림 — 지금 세우면 절반짜리가 됨.

검증 — 인계 48건 통과, 전체 578 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:03:55 +09:00
eomsangdonandClaude Opus 5 c357381355 fix(B08): 품셈 밑수를 원문 본문에서 읽음 — 곱셈 10배·100배 오차 자리
서브 창 제보(기준 단위 없이 도는 단가 125 중 122)의 뿌리. 밑수는 표 안이 아니라
**표 바로 위 본문**에 있음 — `### 12-2. 표면 마무리` 다음 줄의 `(단위: ㎡당)`.
표만 보면 못 찾고, 못 찾은 채 두면 「10㎡당」 표를 1㎡당으로 알아 곱셈이 10배 틀림.

- 원문 md 를 함께 열어 표 위 6줄에서 밑수를 읽음. **본문이 정본, 표 안 긁기는 보조** —
  13-3-1 이 본문 `(단위: ㎥당)` 인데 표 안 긁기가 비고의 `10㎡` 를 물어 오던 자리.
- `(단위: 인/㎡당)` 꼴은 **분모가 밑수** — 값의 단위(인)를 밑수로 읽으면 뜻이 뒤집힘.
- ⚠ 「당」도 「단위:」도 없으면 밑수가 아님. 처음에 조건을 느슨히 두어
  `(무한궤도,0.7㎥)` 를 「0.7㎥당」으로 읽어 5건이 잘못 잡혔음 — 조여서 고침.
- 위로 거슬러 보되 **다른 표에 닿으면 멈춤** — 앞 표의 밑수를 물어 오면 조용히 틀림.

밑수 확보 13 → 183 (그중 묶음 기준 36건: 떼채취 100㎡ · 큰돌쌓기 10㎡ ·
뒷채움 100㎡ · 묘목 1000본 · 헬기방제 160ha 등). 못 찾은 144건은
`basis_missing_2026-01-01.json` 으로 냄 — 곱해야 하는 형태만 담아 잡음을 줄임.
빈칸으로 두면 1단위당으로 오해되므로 목록으로 드러냄.

앞서 확인한 「밑수가 밀렸나」와는 다른 물음이었음 — 이번은 「아예 안 적혔나」.

검증 — 품셈 30건 통과, 전체 575 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 01:02:21 +09:00
eomsangdonandClaude Opus 5 f0608bd315 feat(B09): 2단 열 머리 표 읽기 + 「계」 열 이중계상 차단
- 자원 이름이 **둘째 줄**에 오는 표를 읽음 (철근 12-3:
  `구조별 | 가공 | 조립 | 계` 아래에 `철근공(인) | 보통인부(인)` 세 벌)
- ⚠ 「계」 열은 가공 + 조립을 이미 더한 값이라 **통째로 뺌** — 같이 읽으면
  두 번 셈(㉤ 열 방향 사고 자리). 검산: 간단 = 1.07 + 1.69 = 2.76 으로
  품셈 「계」와 일치
- 머리 줄에는 갈래 칸이 없어 자료 행과 한 칸 어긋나는 것 보정
- 열 머리 되풀이 금지는 **1단 표에만** 적용. 2단 표에서 같은 직종이
  공정마다 되풀이되는 것은 정상이라, 한 규칙으로 뭉치면 철근이 통째로 버려짐
- 내역서 후보 목록에 **규격 갈래**(`#무근구조물`)도 넣음 —
  B08 이 갈래 없는 코드를 보내면 후보로 보이고 임의로 안 고름

철근 현장가공 및 조립 4갈래 성립 (간단 919,146.7 … 매우복잡 1,278,375.3)

검증: pytest 165 통과(신규 2 — 「계」 열 제외 확인 + 되풀이 규칙 짝 시험)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:59:45 +09:00
eomsangdonandClaude Opus 5 7c14d33f97 fix(B08): 운반 표가 영영 0줄이 될 자리 수정
⑬ 운반 사슬 실증 중 발견. 정본에 저장되는 것은 유토곡선 한 벌(`mass_haul`)이고
배분은 그 안의 `haul_plan` 인데, 라우터가 바깥 껍데기를 그대로 넘기고 있었음.
`blocks` 를 못 찾아 운반 표가 항상 빈 표가 됨. [확정] 전에는 어차피 빈 표라
화면에서 티가 안 나던 자리임.

검증은 사용자 프로젝트를 건드리지 않고 서버 계산 코드(Node 번들)를 시험 노선으로
직접 돌려 실제 배분을 받아 확인함.
- 운반거리 표: 무대 3.90㎥/10m · 도자 7.80㎥/40m · 덤프 3,712.29㎥/288.12m
- 인계: 무대 in_bill=false + 수량 그대로, 도자 FP-10-11 · 덤프 FP-10-12
- 검산: 합 3,723.99 vs hauled 3,724.00 (차 0.01 — 배분 자체의 반올림)
- 장비 한계: 무대 20m · 도자 60m 이내 확인

저장 정본 모양을 그대로 넣은 시험 2건으로 못 박음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:56:55 +09:00
eomsangdonandClaude Opus 5 2ad66dce5d feat(B09): 열이 자원인 표(전치형) 읽기 + 오독 3종 차단
품셈에는 자원이 행이 아니라 **열 머리**에 오는 표가 39건 있음
(「구 분 | 콘크리트공(인) | 보통인부(인)」, 행은 무근·철근·소형구조물).
행-자원으로 읽어 콘크리트 타설(12-1)이 하나도 안 서고 있었음

- 갈래(variant)마다 따로 세움 — 품이 달라 뭉치면 어느 것도 안 맞음.
  코드는 `B-FP-12-01-01#무근구조물`, 이름에 갈래를 적어 사람이 고를 수 있음
- 레디믹스트콘크리트 타설: 무근 58,635.0 / 철근 65,826.4 / 소형 117,270.0 원

읽다가 잡은 오독 3종 — 전부 **고쳐 읽지 않고 버림** (자리를 단정할 수 없음)
- 병합 셀로 한 칸 밀린 행: 목재틀흙막이가 등급 글자를 건축목공 자리에 두어
  단가 503만원으로 섰음
- 좌우 두 판이 붙은 표: 소운반에서 거리값 60 이 「보통인부 60인」으로 읽혀
  1,553만원이 섰음. 열 머리가 되풀이되면 표째 버림
- 같은 갈래 이름 중복: 목재틀흙막이 「상」이 8.760 + 13.767 로 합산돼
  667만원이 섰음. 그 줄들을 버림
- 갈래 이름에 자원용 머리글 필터를 쓰던 것도 고침 — 「중」·「상」 등급이
  통째로 지워지고 있었음. 합계 줄만 걸러 냄

기준 단위 미상 드러내기 (막지 않음 — 막으면 125 중 122 가 멈춤)
- `unknown_basis` 122건, 그중 100만원 넘는 10건을 목록으로 냄
  (떼채취 평떼 1,032,408원 등 — 「100매당」류 묶음 기준 의심)
- 내역서 줄 비고에도 「기준 단위가 표에 없습니다 — 수량 단위와 같다고 보고
  곱했습니다」를 적음. 반영률 문구를 덮지 않고 이어 붙임

700줄 제한으로 전치형 처리를 `_Transposed.py` 로 분리 (647 + 138줄)

검증: pytest 163 통과(신규 5). 자원 축 198줄·갈래 54,
일위대가 85 → 125, 분포 최소 233.4 · 중앙 72,268.5 · 최대 5,030,954.6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:53:58 +09:00
eomsangdonandClaude Opus 5 e90a5703ee feat(B08): 준비공·사방공 탭 + 페이지 최종 점검
⑪ 준비공·사방공 — 자리를 만들되 없는 값을 지어내지 않음.
- ⚠ 벌목은 값을 안 냄. 토공집계의 「지장목제거」로 이미 서 있어 또 세우면 같은
  나무를 두 번 벰. 참고 면적만 보이고 「다른 표에서 이미 섬」으로 가리킴.
- 못 서는 줄에 사유를 적음 — 표토제거(두께·구간 미정) · 제근(입목 본수 없음) ·
  규준틀(개소 기준 미정). 공종코드는 미리 적어 둠.
- 사방공은 레지스트리의 실제 type_id 로 봄. 없으면 「해당 없음」 — 0 을 적지 않음.
  이름을 지어내면 영영 안 걸리므로 레지스트리와 대조하는 시험을 둠.

⑫ 최종 점검 — 탭 6장 전수를 실화면에서 돌려 값으로 서는 것 확인
(토적표 65측점 · 토공집계 10줄 · 운반거리 안내 · 준비공 5줄 · 구조물 원단위 10줄 ·
자재총괄 4줄). 전체 566 passed, tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:50:35 +09:00
eomsangdonandClaude Opus 5 8e78940b7a feat(B08): 거푸집 사용횟수 + 공종코드 잇기
- 거푸집 사용횟수는 **관측값이 아니라 법**임. 품셈 1-7-1 이 구조물 종류별로
  정해 둠(옹벽 3회 · 보호공 기초 6회). 원문 문구를 데이터에 싣고 우리 구조물이
  어느 예시에 걸리는지 적음. 걸리는 예시가 없으면 「사용횟수 미확보」.
- ⚠ 횟수별 재료 환산(품셈 12-4 합판 3회 46.1 %)은 **하지 않음**. 그 비율은
  일위대가 재료비에 걸리는 값이라 B08 이 곱하면 B09 와 겹쳐 두 번 줌.
  B08 이 내는 것은 접촉 면적 그대로 + 몇 회짜리인가까지. 시험으로 못 박음.
- 동바리는 슬래브를 떠받칠 때 쓰는 것이라 지금 서는 구조물(옹벽·집수정)은
  대상이 아님. 0 으로 적지 않고 「대상 없음 + 사유」로 냄.
- 거푸집 이름은 정확 일치로만 봄 — 부분일치면 「거푸집씻기」(공사용수)가 걸림.

공종코드 잇기
- 집수정 → FP-12-15, 물넘이포장 → FP-12-06 로 이음.
- 옹벽은 **품셈 12장에 그 이름의 공종이 없음**. 빈 코드로 두면 「매핑을 못 찾은
  줄」과 구별이 안 되므로 `composite` 로 묶음(타설+거푸집+철근+기초잡석)을 적음.
  일위대가 조립은 B09 몫이고 B08 은 물량과 묶음만 넘김.

검증 — 거푸집 8건 · 인계 45건 통과, 전체 558 passed. tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:45:02 +09:00
eomsangdonandClaude Opus 5 8076731f01 feat(B09): 기계 시공능력 공식으로 토공 장비 몫 조립
- 품셈 8-1-4 `Q = 3600÷Cm · q · K · f · E` 구현. 계수는 품셈 표에서 읽고
  q(버킷 용량)는 기종 규격에서 옴. 수량 1단위당 기계 소요시간 = 1÷Q
- 「유압식백호우 (무한궤도,0.7㎥)」를 카탈로그 굴착기(무한궤도) 0.7 로 이음.
  이름·규격이 둘 다 맞을 때만 고름
- 배분율 표는 각 몫을 그 비율만큼만 셈 — 인력 0.23인 × 10 %, 장비 1/Q × 90 %.
  종전엔 인력 원단위가 전량에 곱해져 9 배 부풀 자리였음
- 범위 표기(「0.55∼0.45」)는 확정값이 아니므로 가운데값을 취하지 않고
  `FactorGap` 으로 드러냄. 계수 16 표 성립 / 못 세운 것은 목록으로 남김
- 산출근거 문구를 줄에 적어 사람이 검산 가능
  (예 `Q = 3600 ÷ 18 × 0.7 × 0.9 × 0.77 × 0.60`)

결과: 배분율 미완 25 → 13 공종. 측구터파기 토사 5,288.4원/㎥
(인력 3,957.5 + 장비 1,330.9). 실물 인계자료 내역서 합계 478,680원

㉣ 오탐 방지: 이 모듈은 `reject_efficiency_in_hourly_rate` 를 부르지 않음.
E 는 품셈이 작업량 쪽에 넣으라는 값이라 여기서 막으면 정상 계산이 멈춤.
짝 시험 3건으로 못 박음 — Q 의 E 는 통과 / 사용료의 E 는 차단 /
같은 E 가 두 번 곱해지지 않았는지 금액으로 대조

검증: pytest 158 통과(신규 12). 공식은 손계산과 일치(58.212㎥/hr)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:43:11 +09:00
eomsangdon e7e3f6af1e Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-08 00:41:49 +09:00
eomsangdonandClaude Opus 5 513769796f feat(B08): 콘크리트 구조물 관측 원단위 + 품셈 몫 표시
⑩ 콘크리트 구조물. 품셈에 구조물별 표준 물량표가 없어(구조물_수량.md ·
배수공_수량.md §2) 실무 설계원본 관측값을 데이터 파일로 둠.

- `resources/data_structure_unit/` — 반중력식옹벽 H=2.0(m당) · 돌집수정 ㄷ/ㄴ형 ·
  집수정 Ø800 · 콘크리트포장 T=20cm. 줄마다 출처를 실음.
- 규격 정확 일치로만 씀. 보간하지 않음 — H=1.6 을 H=2.0 에서 줄여 쓰면 틀림
  (기초·벽 두께는 높이에 비례하지 않음). 안 맞으면 「원단위 미확보」로 드러내고
  표에 있는 규격을 함께 알림.
- BOX암거·세월교는 관측값도 없고 저장 제원에 두께가 없어 전개식도 못 세움.
  `not_found` 에 사유와 함께 남기고 미확보로 냄 — 두께를 지어내면 콘크리트·
  거푸집·철근으로 번져 나감.
- 성분마다 `basis_kind`(derived/observed)를 달아 화면에 「치수 전개 / 실무 관측」
  으로 보임. 두 근거가 한 표에 섞이므로 안 적으면 되짚을 수 없음.
- 배수관의 유입부 집수정을 별도 줄로 세움. 품셈도 관부설과 집수정을 다른
  공종으로 둠 — 한 줄로 합치면 어느 쪽 물량인지 못 가름.

품셈 몫 표시 (조율 창 제보 교차 확인)
- `인력(10%)`·`장비(90%)` 처럼 딱지가 비율을 달고 오는 표 25건 확인. 형태 판정은
  정상(requirement)이었으나 **장비 몫이 시공능력 공식이라 값이 아님**을 아무도
  알 수 없었음. `resource_shares`·`partial_ratio` 를 실어 「단가가 일부만 선 것」이
  정상으로 흘러가지 않게 함. 공식 기호는 첫 표에만 있고 뒤 표가 물려받으므로
  기호 유무가 아니라 몫 유무로 판정 — 기호로 세면 절반을 놓침(9-13-2).

반영률 계약 갱신 — breakdown 을 늘 실음(`application_ratio_breakdown` ·
`quantity_breakdown`). `application_ratio_pct` 는 두 율이 같을 때만 채우는 편의값.

검증 — 관측 원단위 17건 · 인계 43건 · 품셈 23건 통과, 전체 548 passed. tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:38:18 +09:00
eomsangdonandClaude Opus 5 a71031b1f0 feat(B09): ④ 예산내역서 조판 + 배분율 표 오독 차단
조판
- B08 인계 응답을 계층 선 내역서로 접음. 계층·정렬은 공종 마스터의
  `parent_code`·`sort_order`(256 간격)에서 옴 — 코드 글자수로 깊이 안 셈
- ITEM NO. 를 가지치기 나무에서 매김. 머리글 줄은 수량·금액 없음
- `in_bill=false`(보정량계)는 수량만 보이고 금액 안 붙임.
  `check_excluded_rows_not_priced()` 가 수치로 막음 (㉡ 확장)
- 단가 없는 줄·공급 구분 미정 자재는 0 으로 안 때우고 `missing` 에 이름째 남김
- 잎에 일위대가가 없고 하위에 있으면 **후보만 보임** — 임의로 고르지 않음
- 반영률은 적기만 함(B08 이 이미 곱함) — 여기서 또 곱하면 두 배

배분율 표 오독 차단 (2026-09-08 실측으로 발견)
- 분류 딱지가 「인력(10%)」처럼 비율을 달고 오면 판정이 빗나가 자원이 통째로
  빠지고 있었음. 측구터파기(FP-09-12-01)는 자원 줄이 0 개였음
- 비율 꼬리표만 떼고 정확 일치 유지 — 「보통인부(인)」·「인력운반공」은 안 걸림
- 자원 축 119 → 144 줄, 일위대가 73 → 85. 기존 줄 변경 0·삭제 0
- ⚠ 그 표들은 인력 몫만 붙음(장비 몫은 시공능력 공식). 그대로 두면
  인력 10 % 몫 단가가 전량에 곱해져 **조용히 틀림** — 25 공종을
  `partial_ratio` 로 표시하고 내역서에서 금액을 안 붙임

검증: pytest 146 통과(신규 10). 가드는 일부러 어겨 멈추는 것까지 확인,
오탐 짝 시험 포함. 실물 인계자료(프로젝트 5cff3920)로 조판 실행 —
14 줄·검산줄 1·미확보 14 건이 이름째 뜸

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:31:17 +09:00
eomsangdonandClaude Opus 5 96c304ac35 fix(B08): 인계 계약 정정 — 반영률 세 칸 · 할증 상태 세 갈래
- 반영률: `quantity`(적용 후) · `quantity_gross`(적용 전) · `application_ratio_pct`
  세 칸을 함께 실음. 곱하기는 B08 한 곳에서만 하고 B09 는 산출근거에만 적음.
  100 % 인 줄도 율을 적음 — 칸이 비면 받는 쪽이 적용 여부를 단정 못 함.
  `verify_ratio_math()` 가 세 값의 정합을 재고 `build_handoff()` 가 실제로 부름.
- 할증 상태를 세 갈래로: `applied` / `not_applied` / `rate_unavailable`.
  두 갈래면 「율을 못 찾아 안 붙인 것」이 「붙였다」로 나가 나중에 진짜 율이
  들어왔을 때 한 번 더 붙음. 옛 깃발은 실제로 붙었을 때만 참으로 둠.
- 관급구분 `unknown` 이 세 번째 계약값임을 주석에 명시. B09 는 관급자재대에도
  도급 재료비에도 넣지 않고 missing 으로 뺌.
- 운반 줄이 실제로 실리는지 시험으로 못 박음(무대 in_bill=false 포함 4줄).

⚠ 반영률 누락 지적은 사실이 아니었음 — 실서버 확인 결과 네 반영률이 전부 100 %
이고, 성토면다짐·층따기가 같은 사면 면적에서 나와 값이 겹친 것임. 곱셈은
정상 동작 중. 다만 세 칸 계약은 이중 적용을 막으므로 그대로 반영.

검증 — 인계 40건 통과, 전체 523 passed. 실서버에서 세 칸 정합·할증 상태
`rate_unavailable`·검산 경고 0 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:26:27 +09:00
eomsangdonandClaude Opus 5 c7d917864c feat(B08): 암 시공법 칸 · 관급구분 표 입력 · 산출 크기 요약
- 암 시공법(ripping/blasting)을 갈래 비율 칸 아래에 붙임. 갈래 이름만으로는
  품셈 공종(암절취 FP-09-04 / 발파암 FP-09-05)을 못 고르던 자리. 기본은
  「안 정함」이고, 안 정하면 인계에서 사유와 함께 드러남. 비율 미입력 상태의
  「암」 한 줄에도 칸을 냄.
- 관급/사급은 표 안에서 줄마다 고름. 관급 줄에만 설치주체가 열리고 사급으로
  되돌리면 잠기며 비워짐. 만진 줄은 표시가 남음.
- 산출 요약(최소·중앙·최대)을 원단위·자재총괄·인계에 붙임. 단위별로 갈라 냄 —
  값이 있기만 하면 시험이 못 잡는 자릿수 어긋남을 사람이 훑게 하는 장치.

화면에서 걸려 고친 것 둘
- 관급을 골라도 설치주체 칸이 잠긴 채 남던 것. 고른 즉시 열고 닫게 함.
- 한 번 고른 시공법을 되돌릴 길이 없던 것. 설정 저장이 병합이라 빈 값을 보내도
  옛 값이 남았음. `save_section(replace_keys=…)` 로 되돌릴 수 있어야 하는 칸만
  통째로 갈아 끼움. 나머지는 그대로 병합.

앞 커밋에서 온 타입 오류 2건도 고침 (`slopeColumnCount` 미사용,
`variant: "outlined"` 는 없는 값). 앞선 확인에서 npx 가 엉뚱한 패키지를 실행해
「오류 없음」으로 잘못 봤음 — tsc 는 config/node_modules 것으로 부를 것.

검증 — 새 시험 5건 + 인계 28건 통과, 전체 509 passed. tsc 오류 0.
화면 실조작으로 시공법 저장·되돌리기, 관급/설치주체 저장·되돌리기 확인 후
검증으로 바꾼 값은 원래대로 복원.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:18:00 +09:00
eomsangdonandClaude Opus 5 01691d4c14 feat(B09): 자원 축 재생성 + 마스터 파일 지문 기록
- B08 공종 마스터 재생성(ab39c174) 반영 — 자원 축 116 → 119 줄,
  미판정 83 → 76. 새로 붙은 것: 강관동바리(형틀목공·보통인부) ·
  신구 BOX접합(미장공)
- 기존 119 줄 중 **수치가 바뀐 줄 0건** — 밑수 판정 교정이 기존 소요량을
  건드리지 않았음. 일위대가 총액 분포도 그대로
  (최소 233.4 · 중앙 86,034.0 · 최대 1,916,466.0, 의심 저가 0건)
- 산출물에 `source_master_file`(마스터 파일 sha256) 추가 — 기존
  `source_dataset_version` 은 품셈 원판 지문이라 마스터를 다시 생성해도
  안 움직여, 낡은 파생물을 드러내지 못했음

검증: pytest 136 통과, 새 공종 2건 일위대가 적층 확인
(강관동바리 27,908.7 · 신구 BOX접합 33,273.1)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:14:02 +09:00
eomsangdonandClaude Opus 5 2b21c2ff5c merge: origin/main_desktop_1 되받기 (B08 공종 마스터 재생성 ab39c174)
로케일 충돌 1건 해소 — B08 구역 줄바꿈 차이뿐, 상대 쪽 서식 채택
(내 B09 키는 자동 병합됨).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:10:24 +09:00
eomsangdonandClaude Opus 5 ca881fbb18 feat(B09): 원가계산 화면에 공종 수량 연결
- 공종 수량(코드=수량)을 일위대가에 곱해 직접비 3분할 생성, ⑤ 원가계산서
  밑수로 투입 — 뭉치지 않고 재료·노무·경비 성분 그대로 전달
- 단가 없는 공종은 0 으로 안 때우고 `missing_unit_prices` 로 화면 노출
- 계산 원천을 화면에 표시 (「수량 원천: 손입력 / 직접 입력」)
- ⑤ 표에 찍히는 값은 자원 집계표 규칙(반올림)으로 절단 — 소수점 유출 제거
- 일위대가 요약에 총액 분포(최소·중앙·최대)·의심 저가 목록 추가

검증: pytest 136 통과, 공용 브라우저(5174) 실측 — 수량 입력 후 재계산 시
재료비 10,478,189 · 순공사원가 84,454,045 · 총공사비 113,652,213,
미매칭 공종 1건 화면 표시 확인

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:09:27 +09:00
eomsangdonandClaude Opus 5 ab39c174aa feat(B08): B09 인계 두 벌 + 품셈 딱지형 표 판정 보완
일감 9. 수량 결과(작업 공종 축)와 자재 총괄(자재 축)을 각각 낸다.

- 자재 줄에는 공종코드를 붙이지 않음. 붙이면 자재가 내역 줄로 오해되어
  이중계상이 됨. `verify_no_code_on_materials()` 로 코드에 못 박음.
- `in_bill` 을 실어 무대·합계 줄을 가름. 수량은 그대로 넘김 —
  무대+도자+덤프 = 총 운반토량 검산에 쓰이는 값이라 빼면 검산이 죽음.
- `ground_class_set` 동봉. 「연암」이 몇 갈래 중 하나인지 알아야 예산내역서가 섬.
- 매핑표는 데이터 파일(`resources/data_work_item_mapping/`). 못 이은 줄은
  빈 코드로 두지 않고 `unmatched_work_items` 로 냄.

품셈 형태 판정 교차 확인 — 「첫 칸이 갈래 딱지이고 이름이 둘째 칸」인 표에서
직종을 첫 세 행에서만 찾아 12장 구조물 표 8건이 미판정으로 빠져 있었음.
딱지를 알아보는 규칙을 맨 마지막에 두어 앞 판정을 흔들지 않고 해소
(미판정 85 → 77, 생산량형 16건 불변). 비율 지시를 먼저 보면 강관동바리 같은
소요량표가 참조로 넘어가므로 소요량을 먼저 보게 순서를 잡음.

검증 — 인계 22건 + 품셈 5건 통과, 전체 500 passed.
실서버 `GET /quantity/handoff` 200 (내역 10줄·제외 1·자재 4, 갈래세트 5).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 00:04:32 +09:00
eomsangdonandClaude Opus 5 991f7e31b1 fix(B09): 분류 딱지 행에서 이름을 둘째 칸으로 읽음 — 자재·장비가 통째로 빠지던 자리
배분 창이 「초류종자살포 배합이 네 일위대가에 있나」를 물어 확인하다 **구멍을 찾음.**

- 품셈 표 중 **첫 칸이 분류 딱지**(「자재」·「장비」)이고 **이름이 둘째 칸**인 것이 있음:
  `['자재', '종      자', '', 'kg', '0.025']` · `['장비', '종자살포기', '2,500-3,000ℓ', …]`.
  첫 칸만 보고 읽어 **그 표의 자재·장비가 통째로 빠지고 있었음** — 씨앗뿜어붙이기에서
  종자·비료·피복제·침식안정제·색소·장비 3종이 다 빠지고 **보통인부 한 줄만** 남았음.
- 첫 칸이 분류 딱지면 이름을 둘째 칸에서 읽고, 값도 그 뒤 칸에서 찾게 고침.
- **매칭 106 → 116**(노무 100 → 110). 산출 파일 다시 냄.

**⚠ 그래도 배합은 아직 못 실림 — 원인은 사급 자재 단가 미결임**
- 종자·복합비료·화이버·합성접착제·색소가 **관급 카탈로그에 없음**(임도 자재가 관급에
  없다는 앞선 발견과 같은 자리). 장비 3종(종자살포기·트럭·물탱크)도 기종 카탈로그에 없음.
- **다만 이제 못 맞춘 목록에는 남음** — 구멍을 목록으로 드러내는 규칙 그대로.
  시험으로 못 박음(`test_seeding_materials_are_listed_not_dropped`).
- ⇒ 사면 4계열의 배합이 **B09 일위대가 몫**인 것은 맞으나, **사급 단가가 들어오기 전에는
  못 세움.** 「자재총괄에 이을 것이 없다」는 메인 판정은 유지되고, 그 자재는
  **㉡ 6번 슬롯 수동 입력 목록**으로 감(야면석·막자갈·고임돌·물구멍에 이어).

자체검증 — 신규 2건 포함 `pytest tmp/tests/ -q` **136 passed** · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:55:09 +09:00
eomsangdonandClaude Opus 5 aecea153e2 fix(B08): 석재 할증률 건설품셈 대조 — 후보와 미확보를 데이터에 명시
조율 창 지적으로 건설공사 표준품셈 1-3-1 을 대조함. 결과를 데이터 파일에
근거와 함께 남김.

- 야면석·고임돌·물구멍 — 산림·건설 두 품셈 모두 이름 없음(`not_found`).
  건설품셈이 다루는 석재는 해상 사석과 원석(마름돌용)뿐이고 제7장 돌공사에는
  재료 할증률표 자체가 없음. 확인한 범위를 `checked` 로 함께 기록.
- 막자갈 — 건설품셈 2호 노반재료에 4 % 로 있으나 우리 쓰임은 뒤채움이라
  조건이 다름. `candidates_pending_user` 에 조건과 함께 두고 엔진은 쓰지 않음
  (지식DB 는 근거 가이드이지 값을 확정하는 곳이 아님).
- 산림품셈 값에는 `pumsem: forest` 표시. 보완 출처를 섞지 않기 위함.
- 물구멍은 공제 대상이 아니라 관 자재임을 코드에 명시. 관 규격(법 Ø3~6㎝ /
  실무 Ø50)과 간격(법 2~3㎡ / 엔진 2.0)이 미확정임을 잠정으로 표시.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:48:28 +09:00
eomsangdon e52f18769c auto: 2026-09-07 23:47 (EOMSANGDON-HOME) 2026-09-07 23:47:47 +09:00
eomsangdonandClaude Opus 5 2faca7ac09 feat(B09): ⑤↔③ 연결 — 일위대가 3분할을 직접비로 접어 넣음 (뭉치지 않음)
**⚠ 일위대가 합계를 순공사비로 뭉쳐 넣으면 ⑤ 의 밑수가 전부 틀림**(8-9 규칙 2) —
산재·고용은 노무비, 건강·연금은 직접노무비, 기타경비는 재료비+노무비를 봄.
일위대가가 3분할을 이미 들고 있으므로 **성분별로 접어 넣음**.

- `direct_cost_from_quantities({공종코드: 수량})` → **직접비 3분할**.
  단가가 없는 공종은 **0 으로 안 때우고** `missing` 에 남김 — 수량이 있는데 단가가
  없으면 그 공종이 총액에서 조용히 빠짐.
- `cost_input_from_quantities()` → 성분이 그대로 `direct_material/labor/expense_krw` 로 감.
- **뭉치면 틀린다는 것을 수치로 보이는 시험**을 둠 — 같은 총액을 노무비 한 덩어리로
  넣으면 건강보험료가 커짐(`test_lumping_would_break_the_bases`).
- ⇒ ④예산내역서 없이도 **수량만 있으면 ⑤ 가 실물로 도는 경로**가 생김.

**자재 잠정(㉡) 화면 표시** — 「사급 자재 단가는 설계자가 직접 넣습니다(6번 슬롯
「적용 단가」) — 유료 물가지 미구독 … (잠정 — 구독하면 1~5번 슬롯에 꽂습니다)」.
화면 실측으로 3줄 다 뜨는 것 확인(일위대가 67건).
- 곁가지 — 화면은 평문이라 `**강조**` 별표가 그대로 보였음. 내보내기 직전에 벗김.

자체검증 — 신규 4건 포함 `pytest tmp/tests/ -q` **133 passed** · ruff 통과 ·
백엔드 재시작 후 화면 클릭 검증.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:45:56 +09:00
eomsangdonandClaude Opus 5 6a487fc6e3 feat(B08): 자재총괄 — 할증이 붙는 유일한 자리
구조물 원단위의 `destination == "material"` 성분만 모아 자재별 합산 후
할증률을 한 번만 적용. 열은 순수량·할증률·합계 + 관급구분·설치주체·비고이며
금액은 없음(B09 경계).

- 할증률은 코드가 아니라 데이터 — `resources/data_material_surcharge/`
  (품셈 1-3-1 재료 할증률 19종 + sha256 매니페스트). 실무 관측값은
  `observed_practice` 로 분리(법대로 원칙).
- 표에 없는 자재는 0 % 로 넘기지 않고 「할증률 미확보」로 표시.
  이름 조회는 정확 일치 — 부분일치면 `막자갈` 이 `자갈` 할증을 뭄.
- 이중계상 방어 ㉠ — 앞 단계 `surcharge_applied` 깃발을 실제로 읽어 경고.
  자재총괄 응답은 `True`, 원단위표는 `False` 로 어느 쪽 값인지 명시.
- 관급/사급 이름은 B09 와 동일(`owner_supplied`/`contractor_supplied`).
  관급 줄에만 설치 주체(`install_by`)를 붙이고, 미지정은 기본값으로
  때우지 않고 드러냄 — 안전관리비 대상액이 「도급자설치 관급금액」이라서임.
- 라우터 `GET /quantity/material-summary` 신설, 화면에 「구조물 원단위」·
  「자재총괄」 탭 추가. `design_owner` 가 붙은 타입(측구)은 중복 계상 방지로 제외.

검증 — 전용 테스트 23건 통과, 전체 회귀 473 passed(기존 B05 깨짐 1건 제외).
공용 브라우저 실조작으로 탭 5장·머리글·값 4줄·미확보 안내 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:43:46 +09:00
eomsangdonandClaude Opus 5 0df6acb57d feat(B09): 자재 카탈로그 — 관급/사급 분리, 그리고 ⚠ 임도 자재가 관급에 없다는 발견
**`B09_Estimation_MaterialCatalog.py` 신설** — 두 창이 맞춘 이름을 그대로 씀:
`supply_type` = `owner_supplied` / `contractor_supplied` ·
`owner_supplied_install_by` = `contractor` / `owner` / `None`.

- 관급 = 나라장터 **6,999건**(`vat_basis: 부가가치세별도` → 부가세 제외 단가).
- **설치 주체는 지어내지 않음** — 원천에 없으므로 전부 `None`(미지정)으로 두고 **세어서
  알림**. 안전관리비 대상액이 관급 전액이 아니라 **도급자설치분**을 쓰므로(8-10)
  잘못 찍으면 금액이 조용히 틀리는 자리임.
- **규격 필수** — 같은 품명에 규격이 수백 개(「연돌」 410 · 「육각볼트」 323). 이름만
  맞추면 엉뚱한 규격 단가가 붙음.
- 매칭 카탈로그에 자재를 붙이면서 **이름 색인**을 넣음(7,700건이라 매번 훑으면 느림).

**⚠ 붙여 보고 알아낸 것 — 관급 목록에 임도 자재가 거의 없음**
- 나라장터 목록이 **건축·설비 자재 중심**(덕트·밸브·복공판·스피커…)이고,
  **시멘트·모래·자갈·레미콘·철근·파형강관이 한 건도 없음**.
- 까닭 둘 — ① 철근·레미콘·아스콘은 그 파일이 `excluded_named_groups` 로 **명시적으로
  빼 둠**(쇼핑몰 스냅샷 미보관) ② **시멘트·모래·자갈은 애초에 사급**임. 실무 울진에서
  관급은 **파형강관 Ø800/Ø1000 뿐**이었음.
- ⇒ **자재 매칭 0건.** 억지로 이름을 느슨하게 맞추지 않음 — 그렇게 하면 엉뚱한
  건축 자재 단가가 임도 공종에 붙음.
- **이 사실을 시험으로 못 박음**(`test_forest_road_materials_are_absent_from_owner_catalog`)
  — 나중에 「자재가 왜 안 붙나」를 다시 파는 일이 없도록.

**화면 요약 문구를 실측대로 갈아 끼움** — 「자재 카탈로그가 없어서」가 아니라
「관급 6,999건을 붙였으나 임도 자재가 거의 없고, **사급 단가가 미결**이라 구조물 계열이
안 섬」으로. 설치 주체 미지정 건수도 함께 냄.

자체검증 — 신규 6건 포함 `pytest tmp/tests/ -q` **129 passed** · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:40:02 +09:00
eomsangdonandClaude Opus 5 3762a06b25 fix(B09): 넓은 필터가 정상 자원 70건을 지우고 있었음 — 조회 우선으로 바꿈
메인의 `막자갈`→배합 `자갈` 오탐 사례를 전해 듣고 내 필터를 재 봤더니 **같은 병이 있었음.**

- **실측** — `is_non_resource_label()` 이 머리글 낱말을 **부분일치**로 보고 있어
  `건설기계운전사`·`일반기계운전사`·`작업반장`·`인력운반공`·`비계공`·`계장공` 등
  **정상 자원 70 / 745** 를 「자원 아님」으로 지우고 있었음(「계」·「작업」·「인력」에 걸림).
  그 탓에 **매칭 14건이 조용히 없어졌음**.
- **고침 둘**
  ① 머리글 판정을 **정확 일치 + 머리글 낱말 조합**(「단위작업별」·「위치및면적」)으로 좁힘.
  ② **카탈로그 조회를 필터보다 먼저** 함 — 카탈로그에 있는 이름은 **정의상 자원**이라,
     필터가 넓어져도 정상 자원이 안 사라지는 구조가 됨.
- **결과** — 매칭 91 → **106**(노무 100 · 기종 6). 산출 파일 다시 냄.

**오탐 짝 시험을 함께 넣음** (「걸려야 한다」 + **「걸리면 안 된다」**)
- 머리글 필터 — 정상 자원 8종은 안 먹고 머리글 7종은 잡는지.
- ㉡ 무대 — 이름에 「운반」이 든 `도자운반`·`덤프운반` 줄은 **안 걸리는지**
  (판정을 이름이 아니라 `equipment` 정확 일치로 하는 근거).
- ㉣ 효율 — 손료계수 0.0002085·조종원 0.125 처럼 **0~1 이지만 효율이 아닌 값**은
  안 걸리는지(효율 자리로 들어올 때만 막는 근거).

**덤으로 잡은 것** — 상세가 하나도 안 붙는 일위대가가 **제목만 서서** 「상세 줄이 없어
단가를 못 조립」하는 상태가 있었음. **붙을 상세를 먼저 모으고 없으면 제목도 안 세움**
(일위대가 67 · 건너뜀 1). 전수 시험(`모든 일위대가 총액 > 0`)이 이걸 잡았음.

PLAN 9-6 에 ㉤(열 방향 검사, 이중계상 규칙이 아니라 표 정합 검사)과 이번 필터 교훈 기록.

자체검증 — `pytest tmp/tests/ -q` **123 passed** · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:33:42 +09:00
eomsangdonandClaude Opus 5 f2d12afef7 feat(B09): ㉤ 열 방향 검사 추가 — 행 검사로 안 잡히는 이중 합산
배분 창이 보고 숫자를 더해 보고 **노무 열만 5,162 어긋남**을 짚음. 확인 결과:

- **값은 멀쩡했음. 내 보고가 줄 하나를 빠뜨린 것**(「제근」 본표는 보통인부 줄이 **둘**임 —
  굴착기 규격 2종에 각각 붙음). 실제 열 합은 재료 20,956.3 · 노무 54,943.2 ·
  경비 22,393.4 · 합계 98,292.9 로 표시와 **전건 일치**.
- **다만 지적한 「열 방향 검사가 비어 있다」는 실재했음.** `TC = NC + GC + JC` 는
  **행 방향** 검사라, 같은 성분을 두 층에서 세는 어긋남(행마다는 다 맞고 열 합만 갈림)을
  못 잡음. 예 — 기계 줄 안에 든 조종원 노무가 별도 노무 줄로도 서는 경우.

- `check_column_sums()` 추가하고 `detail_of()` 에서 **실제로 호출**. 표시 합계가 상세 줄의
  열별 합과 다르면 멈춤.
- 테스트 둘 — ① 노무 열만 부푼 모양을 만들어 **행 검사가 통과하는데 열 검사가 잡는지**
  확인 ② 실제 「제근」 본표가 열 검사를 통과하고 보통인부 줄이 둘인지 확인.

⇒ 이중계상 감시가 넷(㉠할증·㉡무대·㉢배합·㉣작업효율) + **방향 검사 ㉤** 로 늘어남.

자체검증 — 신규 2건 포함 `pytest tmp/tests/ -q` 118 passed · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:26:35 +09:00
eomsangdonandClaude Opus 5 fc40265dbe feat(B08): 구조물 원단위 전개식 — 치수에서 성분 물량
일감 6 엔진. PLAN 8-6·8-8·8-15.

식을 발명하지 않음 — 실무 원본을 옮김
  울진 설계원본 5. 구조도(기번3).xlsx 에 구조물 31종 계산식이 살아 있는 수식으로
  남아 있음(8-15). 그 식을 옮기되 식 안에 상수로 박혀 있던 값(돌 뒷길이 0.45 ·
  고임돌 0.15 · 돌중량 0.88 등)은 **계수표로 뺌** — 뒷길이가 바뀌어도 식을 안 고침.
  실무 방식의 약점을 여기서 고친 자리.

치수 정본은 하나 — structures.json 의 type_id·options 를 읽어 계산.
  자기 치수표를 따로 들지 않음(도면은 H=1.5 인데 수량은 옛 치수로 도는 사고 방지).

⚠⚠ 이중계상 셋을 코드로 막음
  ㉢ 배합을 분해하지 않음 — 콘크리트 ㎥·모르터 ㎥ 에서 멈춤. 시멘트·모래로 쪼개는
     것은 B09 일위대가 몫이고 양쪽이 쪼개면 시멘트가 두 배가 됨.
     verify_no_mix_components() 가 산출물에 배합 성분이 나타나면 알림.
     ⚠ 정확히 같은 이름으로만 봄 — 부분문자열로 재니 「막자갈」(뒤채움 재료)이 배합
     「자갈」로 오탐됐음. 개발 중 실제로 걸린 자리라 시험에도 박음.
  ㉠ 할증을 안 붙임 — surcharge_applied=False 로 응답에 못 박음. 할증은 자재총괄 한 곳뿐.
  · 성분마다 destination 을 담 — earthwork(토공 대분류로 합산) / material(자재총괄) /
    unit_price(일위대가 재료비). 울진 토적집계 D12~D14 가 구조물 터파기 1,248/30㎥ ·
    되메우기 739㎥ 를 개별 줄이 아니라 토공으로 합산한 것이 근거.

⚠ 공제 규칙을 주석에 원문째 — 품셈 1-2-1 「물구멍·이음줄눈·모따기·철근은 공제하지
  않음」. 치수를 곧이곧대로 빼면 실무값과 어긋남. 빼는 것은 관 통과 단면처럼 실제로
  비어 있는 자리뿐.

원본에 없는 칸은 지어내지 않음 — 뒷길이 60㎝ 돌중량이 원본 표에 비어 있어 야면석을
  내지 않고 그 사실을 notes 로 알림(8-8 ㉮).

⚠ 실무값과 0.012 차이 — 우리 쪽이 맞음
  터파기 1.5375 vs 실무 1.55. 평균두께가 0.825 인데 실무 시트는 표기값 0.83 으로
  다시 계산해 1.545 가 됨. 품셈 1-2-2 는 표기 규칙이고 계산은 전정밀(8-16).
  맞추려고 식을 비틀지 않고 허용오차를 그 차이만큼 둠.

검증 — 17건 통과. 배합 금지·할증 금지·행선지 표시·계수표 교체·빈 칸 미생성을 각각 못 박음.
  실데이터(route 150 의 돌쌓기(찰) H=2.5·L=10)로 확인 — 성분 10개, 배합 위반 0.
  전체 회귀 450 passed (실패 1건은 B05 코리도 기존 깨짐).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:25:34 +09:00
eomsangdonandClaude Opus 5 29cf61f4ad feat(B09): 일위대가 금액 0.1원 버림 — 자리 규칙 적용 + 끝자리 차이를 화면에 드러냄
- `OutputPlace.UNIT_PRICE_ROW` 추가 — **0.1원 미만 버림**(품셈 1-2-2 「일위대가 금액란
  0.1원 미만 버림」). 계산은 전정밀, **표 그리는 자리에서만** 자름(단수는 출력 위치에 붙음).
- **행 합계 = 자른 성분 셋의 합** — 그래야 표에서 `TC = NC+GC+JC` 가 섬. 전정밀 합을
  따로 자르면 성분과 합계가 어긋나 보임. 표 전체 합계도 **행별로 자른 값을 더함**
  (`단수처리_규칙.md` §2 「행별 처리(합계 후 아님)」).
- ⚠ **행별 절사로 생기는 끝자리 차이를 숨기지 않음** — 화면에 「행별로 0.1원 미만을 버려
  합계 끝자리가 다릅니다 (정상). 자르기 전 합계: …」를 띄움. 숨기면 나중에 「합계가 안
  맞는다」며 계산을 고치려 듦.

**화면 실측(5174)** — 일위대가 탭 → 목록 64건 → 「제근」 본표 →
`▸ 굴착기(무한궤도) 0.2 | 기계경비(105) | hr | 0.8 | 8,936.5 | 28,329.7 | 11,098.5 | 48,364.7`
→ 기계 줄 클릭 → **시간당 사용료 본표로 파고듦**(손료 13,873.1 · 경유 11,170.6 ·
건설기계운전사 35,412.1 = 60,455.9). 합계 98,292.9 / 자르기 전 98,293.2 표시.
산출 요약 2줄(자재 미확보로 구조물 계열 안 섬)도 탭 상단에 뜸.

⚠ 검증 함정 기록 — 해시 라우트에 쿼리를 붙일 때 `#/b09-estimation?v=2` 는 라우트가
안 잡힘. **`?v=2#/b09-estimation` 처럼 해시 앞에** 붙여야 함.

자체검증 — `pytest tmp/tests/ -q` 116 passed · ruff 통과 · tsc(B09) 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:23:09 +09:00
eomsangdonandClaude Opus 5 564cb11103 feat(B08): 수량산출 화면 — 탭 3장·산출 조건 입력·[저장]
일감 4·5 화면 마무리. 앞 `auto:` 커밋(4756f0fe)은 토적표 그리드·로케일 배선분임.

우측 — 실무 산출서 시트를 탭으로
  토적표 · 토공집계 · 운반거리. 탭을 눌러 갈아 끼움.
  토공집계 열은 거창 실무 시트 그대로(구분·공종·규격·단위·계·비고).
  운반계획이 아직 없으면 빈 표 대신 「종단설계에서 확정하면 생김」을 보임.

좌측 — 산출 조건 입력
  ⚠ 암 갈래 칸 수를 코드에 안 박음 — 프로젝트 세트가 정함(지금 거창5 = 5칸).
  반영률 4칸, 기본 100 %. 실무 관측 80/50/80 은 기본값 아님(PLAN 8-11 · 법대로).
  반영률 이름을 사람 말로 보임 — 서버 키(fill_slope_compaction)를 그대로 띄우면
  설계자가 못 읽음. 로케일 키 4개 추가.

[저장] 신설 (CLAUDE.md 5장 데이터 3층)
  조작은 캐시에만 쌓이고 [저장]에서만 정본(project_settings.json)으로 감.
  자동저장 안 만듦. quantity 구획만 감 — estimation 은 B09 것이고 서버가 막고 있음.
  저장 뒤 표를 다시 받아 그림 — 조건이 바뀌면 집계·운반 값이 달라짐.
  저장 안 한 변경이 있으면 나갈 때 알림(beforeunload).
  ⚠ [초기화]는 안 달았음 — 5장의 [초기화]는 초기값을 작업본에 덮어쓰는 것인데
  설정에는 대응 초기값이 없어 재계산 단추로 오해될 자리임. TODO(미결) 주석으로 남김.

검증 — 공용 브라우저 실화면, 타입검사 0.
  탭 3장 · 입력칸 9개(암 5 + 반영률 4) · 단추 [저장][확정].
  토공집계 10행, 값 일치 — 흙깎기 토사 2,526.99 · 측구 90.51 · 보정량계 6,511.06.
  [저장] 실제로 눌러 확인 — 연암 60·보통암 40 을 넣으니 암 3,512.09 가
  2,107.25 / 1,404.83 으로 갈리고(60:40 안분), 성토면다짐 80 % 를 넣으니
  13,518.6 → 10,814.9 로 바뀌며 비고에 「성토면 80 % 반영」이 남음.
  ⚠ 넣었던 값은 전부 원래대로(0·0·100) 되돌려 놓았고 화면도 사용자가 보던 주소로 복귀.
  ⚠ 개발 중 발견 — vite 가 옛 모듈을 물고 있어 첫 확인이 옛 화면이었음.
  쿼리를 붙여 새로 물린 뒤 재확인(계획서 7-2 의 그 함정).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:18:10 +09:00
eomsangdon 9eeea3bc59 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-07 23:13:51 +09:00
eomsangdonandClaude Opus 5 20018c6739 style(B09): 라우터 import 정렬
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:13:47 +09:00
eomsangdonandClaude Opus 5 6c4d0251db feat(B09): 일위대가 탭 — 목록표+본표 2단, 원천 표시, 층 파고들기
**API 2종** (`B09_Estimation_Router.py`)
- `GET …/estimation/unit-prices` — **목록표** + **산출 요약**. 요약을 같이 보내는 까닭은
  사용자가 「무엇이 안 선 상태인가」를 화면에서 알아야 하기 때문임(자재 카탈로그
  미확보로 구조물 계열이 안 섬).
- `GET …/estimation/unit-prices/{code}` — **본표**. 줄마다 원천(자재5·노임6·기계경비105·
  일위대가103·단가산출104)과 **파고들기 가능 여부**가 붙음. 없는 코드는 404.
- 품셈 3 MB 를 요청마다 다시 안 읽게 `cached_build()` 로 한 번만 조립.

**화면** (`B09_Estimation_UI_Page.ts`)
- 일위대가 탭 활성화. **목록표(위) + 본표(아래) 2단** — 9-3 「제목+상세 한 쌍」이 화면에도
  그대로 섬.
- 본표 줄마다 `원천(번호)` 표시, **기계 줄을 누르면 그 시간당 사용료 본표로 파고듦**
  (거기서 취득가·연료·조종원까지 보임). 값을 못 믿을 때 사람이 하는 일이 이것임.
- **재료·노무·경비 3분할 + 합계 줄**, `TC = NC + GC + JC` 성립 여부를 화면 문구로 냄.
- **산출 요약을 화면에 표시** — 자재가 없어 구조물 계열이 못 선다는 것을 그 자리에 적음.

locale 은 **B09 키만** 추가(16줄), 공용 파일 다른 줄 무수정.

⚠ 화면 조작 검증은 다음 단계 — `tsc` 는 통과했고(남은 오류 1건은 메인 창 B08 파일),
백엔드 재시작·클릭 검증은 이어서 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:13:20 +09:00
eomsangdon c0cd398eea Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-07 23:12:53 +09:00
eomsangdon 4756f0fe72 auto: 2026-09-07 23:12 (EOMSANGDON-HOME) 2026-09-07 23:12:41 +09:00
eomsangdonandClaude Opus 5 1789b26563 feat(B08): 토공집계표·운반거리 그리드 모듈 신설 (아직 미등록)
일감 4·5 화면의 표 부품. 이 커밋 시점에는 **아무 데서도 import 하지 않음** —
「신규 파일 먼저, 등록 줄 나중」 두 걸음 규칙(공용 파일 동시 편집 사고 예방).

토공집계표 — 열은 거창 실무 시트 그대로(구분·공종·규격·단위·계·비고).
  같은 구분이 이어지면 한 번만 적음(실무 시트가 병합해 두는 자리).
  비고에는 설계자가 정한 값만 남음 — 반영률을 바꿨을 때, 비율 합이 100 이 아닐 때.
  기본값 그대로면 비움. 안내가 매번 뜨면 잡음이 됨.

운반거리 — 내역 줄(가중평균)과 근거 구간 수를 함께 보임.
  ⚠ 무대 줄에 「내역 제외」 표시 + 「품셈 1-2-7 소운반 20m 이내는 품에 포함」을 적음.
  「제외」만 있으면 빠뜨린 것으로 오해됨. 규칙이 코드에만 있으면 잊히므로 화면에 남김.
  운반계획이 아직 없으면(=[확정] 전) 빈 표 대신 「종단설계에서 확정하면 생김」을 보임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:12:32 +09:00
eomsangdon 0262b0f3be Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-07 21:59:08 +09:00
eomsangdonandClaude Opus 5 28e4aed499 feat(B08): 토공집계표·운반표를 API 에 붙이고 산출 조건 저장 자리 신설
일감 4·5 의 API 덩어리.

한 응답에 실음
  earthwork-table 이 토적표(체적) · slope(사면 4계열) · haul(운반 가중평균) ·
  summary(토공집계표) · settings 를 함께 냄. 실무 산출서가 한 벌로 움직이는 값이라
  나눠 부르면 같은 측점 목록을 여러 벌 들게 됨.

운반계획이 없으면 빈 표가 정직함
  운반계획은 [저장]·[확정]에서 정본에 남는 값임(longitudinal.data.mass_haul).
  아직 안 돌린 프로젝트는 없음 — haul_available 로 알려 화면이 「확정하면 생김」을
  말할 수 있게 함. 지어내지 않음.

산출 조건 저장 — PUT /quantity/settings
  ⚠ 자동저장이 아님(CLAUDE.md 5장). 조작은 캐시에 쌓이고 여기서만 작업본으로 넘어감.
  ⚠ quantity 구획만 씀 — estimation 은 B09 것이고 모듈이 구조로 막고 있음.
  보내지 않은 칸은 저장분을 그대로 둠.
  프로젝트 경로를 못 찾아도 조회는 기본값으로 서게 함 — 설정은 계산을 거드는 값이지
  없으면 못 도는 값이 아님.

암 비율 안분을 비고에 드러냄 (조율 창 지적)
  설계자가 60/30 을 넣으면 총량 보존을 위해 66.7/33.3 으로 안분됨. 값이 말없이
  바뀌는 것이라 「입력 합 90 % → 100 % 로 안분」을 비고에 남김. 합이 100 이면
  비고를 비움 — 제대로 넣었는데 안내가 뜨면 잡음이 됨.

검증 — 회귀 433 passed (실패 1건은 B05 코리도 기존 깨짐).
  엔드포인트 3종 등록 확인(earthwork-table 2 + settings 1).
  안분 비고는 시험으로 못 박음(합 90 이면 비고 있음 · 100 이면 없음).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:58:34 +09:00
eomsangdonandClaude Opus 5 856be1f45b feat(B09): ③단가산출·일위대가 조립 + 네 번째 이중계상 규칙(㉣) 가드
**㉣ 작업효율을 두 곳에 넣지 않는다 — 계획서에 없던 네 번째 규칙**
- 품셈은 작업효율 `E` 를 **작업량 산정**에 넣음(`Q = n·q·f·E`, 8-1-4). 시간당 사용료는
  **1일 8시간**으로 나눔(8-1-6 「관리비는 1일 8시간 초과해도 8시간으로 계산」).
  **양쪽에 넣으면 같은 효율이 두 번 곱해짐.**
- 실무 관측이 효율을 사용료 쪽에 미리 넣어 두어(굴착기 0.7㎥ 노무 55,700/hr =
  일당 ÷ 5.09h), 그 값을 보고 나눗수를 고치는 사고가 정확히 이 자리에서 남.
- `reject_efficiency_in_hourly_rate()` · `check_operator_hours_basis()` 추가하고
  **`hourly_machine_cost()` 안에서 실제로 호출** — 함수만 있고 안 부르면 없는 것과 같음.
- PLAN 9-6 의 금지 규칙을 셋 → **넷**으로 늘리고 근거·경위 기록.
  기계 사용료 11 % 차이는 **「설명된 차이」**임을 표로 못 박음(맞추려 손대지 말 것).

**`B09_Estimation_UnitPrice.py` 신설 — 층을 실제로 쌓음**
- `S 취득가 · L 노임 · M 연료 → X 시간당 사용료 → B 일위대가`.
  **시간당 사용료를 미리 계산해 넣지 않음** — 쌓아야 화면이 「무엇으로 이루어졌나」를
  보일 수 있음(8-13 계산 과정을 감추지 않음).
- 조종원 수량 = `인/일 ÷ 8` 로 상세에 들어가 ㉣이 데이터로도 드러남.
- 자원이 하나도 안 붙은 공종은 **빈 줄로 안 세움** — 0 원 일위대가가 내역에 서면 안 됨.
- ㉠ 가드를 `verify_surcharge_once()` 로 실제 호출 — 일위대가 재료비는 **할증 전** 값.

산출 — 제목 **189**(일위대가 64 · 기계 층 3 · 노임·연료), 건너뛴 공종 0.
실측 `X-0201-0070` 굴착기 0.7㎥ = 재 26,130 + 노 35,412 + 경 24,554.

자체검증 — 신규 5건 포함 `pytest tmp/tests/ -q` **116 passed** · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:58:11 +09:00
eomsangdonandClaude Opus 5 9badcbf80e feat(B08): 토공집계표·운반 가중평균 엔진 + 프로젝트 설정 자리
일감 4·5 의 엔진 덩어리. PLAN 8-11·8-3·8-7.

프로젝트 설정 (common_util/common_util_project_settings.py)
  자리 = <project_root>/project_settings.json, 루트의 project_manifest.json 옆.
  매니페스트 stages 는 단계 산출물 목록이고 설정은 두 페이지가 함께 읽는
  프로젝트 값이라 단계 폴더에 넣으면 주인이 애매해짐.
  구획 quantity(B08) · estimation(B09) — 남의 구획은 읽기만.
  ⚠ 경계를 코드로 막음 — save_section() 이 이름 붙은 한 구획만 갈아 끼우고
  통째로 쓰는 함수를 두지 않음. 두 페이지가 같은 파일을 쓰므로 통째로 덮으면
  상대 값이 사라짐(오늘 main.py 에서 같은 모양의 사고를 이미 겪음).
  ⚠ *_override 기본은 None — 「안 정했으면 config 정본을 씀」. 기본값을 복사해
  넣으면 정본이 바뀌어도 옛 프로젝트가 안 따라옴.
  ⚠ dataset_versions 는 기록이지 정본이 아님 — 되살릴 때 쓰는 정본은 프로젝트
  스냅샷이고 어긋나면 스냅샷이 이김.
  estimation.rate_dataset 은 「연도」가 아니라 판을 가리킴 — 제비율은 연중에도
  개정됨(현행판 2026-04-13). B09 확정 반영.

토공집계표 (열 = 구분·공종·규격·단위·계·비고, 거창 실무 시트 그대로)
  ⚠ 암 갈래 개수를 코드에 안 박음 — 울진 2 · 거창 5 · BOM 1 로 공사마다 다름.
  프로젝트 설정의 세트를 받아 그만큼 줄을 냄. 비율이 아직 없으면 지어내지 않고
  「암」 한 줄로 냄. 비율 합이 100 이 아니어도 준 비율끼리 안분해 총량 보존.
  ⚠ 반영률 기본 100 % — 실무 관측 80/50/80 은 참고이지 기본값 아님(법대로).
  바꾼 경우에만 비고에 「80 % 반영」이 남음 — 누가 정한 값인지 보이게.

운반 가중평균 ((운반수단 x 지반유형)별 1줄)
  실무 울진이 도자 토사/암 · 덤프 토사/암 4줄을 올림. 가중평균 = Σ(토량x거리)/Σ토량
  으로 단순평균이 아님. 개별 구간은 근거로 함께 냄 — 어느 구간이 그 평균을
  만들었는지 되짚게.
  입력은 이미 있는 HaulPlan — 띠·장거리이동에 equipment·haul_distance_m·
  지반유형별 물량(ea/rr/br)이 다 있어 다시 세지 않음.
  ⚠ 무대(free_haul)는 in_bill=False — 품셈 1-2-7 로 품에 포함이고 인력운반 10-6
  도 「소운반 20m 초과분」이라 붙일 단가가 없음. 값은 내되 내역 줄만 뺌 —
  「무대+도자+덤프 = 총 운반토량」 검산에 씀.

검증 — 28건 통과 (토공집계·설정 19 + 운반 9).
  가중평균이 단순평균과 다름을 못 박음(도자 토사 600@40+400@50 → 44m, 45 아님).
  울진2·거창5 두 세트가 같은 코드로 서는 것, 무대가 내역 줄이 아닌 것,
  구획을 따로 저장해도 상대 값이 안 지워지는 것을 각각 시험함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:54:23 +09:00
eomsangdonandClaude Opus 5 c7554d8333 fix(B09): 조종원 노임 나눗수 8시간을 품셈 근거로 못 박음
배분 창이 「관측 55,700 ÷ 일당 283,297 = 5.09시간」을 짚어 확인함. 품셈을 뒤진 결과:

- **8시간이 맞음** — 품셈 8-1-6 기계손료 단서 「관리비는 1일 8시간 초과해도 8시간으로
  계산」이 1일을 8시간으로 잡고, 노임표도 `hours_per_day: 8` 임.
- **관측이 짧은 나눗수를 쓴 것으로 보임** — 작업효율 `E`(실작업시간율)를 시간당
  사용료에 미리 반영한 것. 품셈에서 작업효율은 **작업량 산정**(`Q = n·q·f·E`, 8-1-4)에
  들어가지 시간당 사용료에 안 들어감. **관측에 맞추려 나눗수를 바꾸지 않음**(★법대로).
- ⇒ **조종원 직종 의문은 해소됨** — 직종(건설기계운전사)은 맞고 나눗수만 달랐음.
  잠정 표시는 「직종명이 품셈 8-4 표에 없음」 사유로만 남김.
- 근거·경위를 상수 주석에 적고 테스트로 박음(`OPERATOR_HOURS_PER_DAY == 8`,
  관측값보다 작다는 것까지 확인).

자체검증 — `pytest tmp/tests/ -q` 110 passed · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:53:14 +09:00
eomsangdonandClaude Opus 5 4e6e588b9b feat(B09): 운전경비(연료·잡재료·조종원)를 품셈 8-4 에서 뽑아 시간당 사용료 완성
**빈 두 성분을 채움** — `mach_fuel_rate`·`mach_operator_map` 0건이라 시간당 중기사용료의
재료비·노무비가 비어 있던 자리(손료만 있으면 실제의 4분의 1). 건설품셈 **8-4 운전경비
산정** 표에서 뽑아 **92 기종** 확보.

⚠ **표가 열 단위로 뭉쳐 옴** — PDF 추출 탓에 `분류번호` 한 칸에 코드 31개,
`주연료` 한 칸에 값 31개가 공백으로 이어 붙어 있음. **위치로 짝짓되 개수가 안 맞으면
그 줄을 통째로 버림**(10건 버림, 목록으로 남김). 어긋난 채 짝지으면 **다른 기종의
연료값이 조용히 붙는** 자리라 억지로 안 맞춤.
- 코드는 앞자리를 이어받음 — `0201-0012 0020 0040` → `0201-0020`·`0201-0040`.
- ⚠ **이름 칸은 못 씀** — 표 전체 기종명이 한 덩어리로 옴. **코드로 613 기종
  카탈로그에서 이름·규격을 가져옴**(카탈로그에 없는 코드는 버리고 목록에).

**검증(굴착기 무한궤도 0.7㎥)** — 주연료 11.6 ℓ/hr · 잡재료 22 % · 조종원 1인/일.
시간당 = 재료 26,130(연료 21,418 + 잡재료 4,712) + 노무 35,412 + 경비 24,554 = **86,096**.
STC 2024 관측 96,829 과 **11 % 차** — 손료·재료는 근사하고 **노무가 갈림**
(관측 55,700/hr = 445,600/일로 어느 직종보다 높음). **맞추려 손대지 않음** — 관측값은
대조용이고 우리는 법대로 감. ⚠ 조종원 직종 확인 항목으로 올림.

- **잠정 표시** — 조종원 **직종명이 품셈 8-4 에 없어** `aliases` 기반 규칙(트럭 계열 =
  화물차운전사, 그 밖 = 건설기계운전사)으로 매김. 결과에 `operator_mapping_is_provisional`
  로 드러남. TODO(미결 9-6).
- **유가는 전국평균 잠정**, 지역 파라미터 자리(`region`)만 뚫어 둠 (품셈 8-1-7 5호).
- **파생 파일로 냄** — `resources/data_cost_machine_operating/`, 기준자료 안 건드림.
  `derived_from` 에 `mach_base` 판의 세 쪽을 적어 낡으면 드러나게 함.

자체검증 — 신규 6건 포함 `pytest tmp/tests/ -q` **110 passed** · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:49:50 +09:00
eomsangdon 33331e4364 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-07 20:45:34 +09:00
eomsangdon 4226bf5bd6 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-07 20:45:32 +09:00
eomsangdonandClaude Opus 5 0cc3c1bd5e fix(B08): 잘림 안내를 「주의 + 접히는 목록」으로 — 늘 뜨는 경고라
B06 담당(랩탑) 실측 회신 반영. 미교차는 드문 예외가 아니라 상시임 —
route 169 는 22/105(21 %), 이 노선은 17/65(26 %). 매번 요란하면 곧 무시당함.

고친 것 셋
  · 붉은 오류 → **주의 표시**(테두리를 danger 에서 muted 로). 잘린 줄 표시도 같게.
  · 목록을 `details` 로 **접어 둠**. 필요할 때만 폄.
  · 문구를 **한 덩어리**로 묶음 — 절·성토 면적 · 사면길이 · 사면적이 전부 같은 사유로
    잘리므로 항목마다 따로 띄우지 않음.

안 넓히는 까닭 (주석에 남김)
  미교차 절반 이상이 계곡·절벽처럼 지형이 설계 사면에서 멀어지는 자리라 반폭을 늘려도
  영원히 안 닫힘. 닫히는 쪽도 중앙값 +3m 인데 꼬리가 +292m 이라 전역 확대는 값이 안 남.
  2026-09-03 사용자 확정 「경고로 대체」가 그대로 유효함.

정본 키 대조 — StationSlope.unclosed 가 design.slope_unclosed 에서 나오는 것 확인
  (B06_Section_Engine_Design.py:807 이 내는 그 값).

검증 — 공용 브라우저 실화면. 타입검사 오류 0.
  접힌 상태 `details[open=false]` · 요약 문구 한 줄 · 측점 단추 17개.
  펴면 목록이 보이고, 첫 단추(NO.13)를 누르면 그 줄로 가서 강조됨.
  조작한 화면은 사용자가 보던 주소로 되돌려 놓음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:45:20 +09:00
eomsangdonandClaude Opus 5 fbd7380b83 feat(B08): 토적표에 사면 4계열 열·잘림 경고 붙임
일감 3 의 API·화면 덩어리. 토적표 오른쪽 절반(실무 V~AI)이 화면에 섬.

한 응답으로 냄
  실무 토적표가 한 장이라 화면도 한 장임. 나눠 부르면 두 번 왕복하고 같은 측점
  목록을 두 벌로 들게 됨. earthwork-table 응답에 slope 를 함께 실음.

열 구성 — 계열 4 x 성토면/절토면 = (거리, 면적) 7쌍
  층따기[성토면] · 면고르기 · 법면보호공(종자파종) · 지장목제거.
  거리 = 그 측점 사면길이, 면적은 토적표와 같은 평균단면적법.
  합계행은 면적만 — 거리(사면길이)는 합이 뜻이 없음(실무 시트도 비움).

⚠ 잘린 측점을 감추지 않음 (PLAN 8-4b)
  사면이 원지반을 못 만난 측점은 사면길이·면적이 그 지점에서 잘려 있음.
  경고 문구에 **무엇이 잘렸는지**를 적고(「실제 값은 이보다 큼」), 측점 단추를
  누르면 그 줄로 가서 잠깐 강조됨 — 17곳을 눈으로 찾게 하지 않음.
  잘린 줄은 측점 칸 왼쪽에 표시가 남음.

검증 — 공용 브라우저 실화면. 타입검사 오류 0.
  머리글 3단 13/12/28셀, 대분류에 층따기·면고르기·법면보호공·지장목제거,
  3단에 거리/면적 쌍. 본문 65행 x 34열(토공 20 + 사면 14).
  NO.1 사면값 6.4/64.4 · 3.5/93.0 등이 엔진값과 일치.
  잘린 줄 17개 표시 · 경고 문구 · 측점 단추 17개 확인.
  합계 면고르기 성토 13,518.6㎡ · 절토 5,433.7㎡ 로 엔진 합계와 같음.
  회귀 404 passed (실패 1건은 B05 코리도 기존 깨짐).
  조작한 화면은 사용자가 보던 주소로 되돌려 놓음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:43:20 +09:00
eomsangdonandClaude Opus 5 34d90a14d1 feat(B09): 기계경비(S→X) + 기종 카탈로그 매칭 (PLAN 9-3)
**`B09_Estimation_MachineCost.py` 신설 — 취득가 → 시간당 사용료 두 단계**
- `mach_base_2026.json` 실사: `mach_price` **613 기종**(`machine_code`+`specification`),
  `mach_loss_coef` 387건(시간당 손료계수·내용시간·연간표준시간).
- **시간당 손료 = 취득가 × 손료계수.** 취득가가 **천원 단위**라 환산을 놓치면 1,000배
  틀리는 자리 — 테스트로 박음. 굴착기(무한궤도) 0.7㎥ → **24,554원/hr**
  (STC 2024 관측 23,128 과 같은 자릿수, 취득가 판 차이로 설명됨).
- ⚠ **빠진 성분을 0 으로 안 때움** — `mach_fuel_rate`·`mach_operator_map` 이 **0건**이라
  연료(재료비)·운전사(노무비)를 못 채움. `HourlyMachineCost.gaps` 로 **공백을 알림**.
  실측 비중이 노무 53·재료 20·경비 27 % 라 손료만 쓰면 **4분의 1**이 됨 — 조용히
  총액에 섞이면 안 되는 자리.
- 손료계수 없는 기종 **230건**도 0 으로 안 때우고 멈춤.

**자원 축에 기종 매칭 추가**
- 기종 셀은 괄호 안에 **형식과 규격이 섞여** 있음 — 「굴착기(무한궤도, 0.7㎥)」는
  이름 `굴착기(무한궤도)` + 규격 `0.7`. 형식은 이름의 일부이고 숫자 조각만 규격임.
- **규격이 옆 칸에 있는 표**가 많아(`['굴착기 (무한궤도)','굴착기(무한궤도,0.2㎥)','hr',…])
  뒷 칸에서 규격 후보를 찾아 고름. 규격 없이 매칭 성공으로 치지 않는 규칙은 그대로.
- 산출 갱신 — 매칭 86 → **91행**(노무 86 + 기종 5), 미매칭 274 → **269건**.

자체검증 — 신규 7건 포함 `pytest tmp/tests/ -q` **104 passed** · ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:43:19 +09:00
eomsangdonandClaude Opus 5 6e7570eba6 feat(B09): 자원 축 매칭 — 공종 축에 자원을 붙여 별도 파일로 냄 (PLAN 8-6·9-3)
메인 창의 공종 축(`resources/data_work_item_master/`)은 **읽기만** 하고, 결과는
**내 소관 경로** `resources/data_cost_resource_axis/` 에 냄 — 메인이 품셈을 다시
돌리면 그 폴더가 덮이므로 안에 섞으면 사라짐.

- ⚠ **`pum_form` 을 먼저 봄** — `productivity` = 1 ÷ 값 · `requirement` = 값 ÷ 기준수량.
  **뒤집으면 20배 틀림.** 형태를 모르는 표(`undetermined`)는 **값을 안 씀**, 한쪽으로
  단정하지 않고 멈춤. 테스트가 뒤집힘을 자릿수로 잡음.
- `coefficient`(19)·`reference`(94)는 공종이 아니므로 **일위대가 항목으로 안 세움**.
- **규격이 없으면 매칭 성공으로 안 침** — 「굴착기」와 「굴착기 0.7㎥」는 단가가 다름.
  이름이 여럿이면 규격 없이는 못 고르고 `unmatched` 로 감.
- **못 맞춘 것은 빈칸이 아니라 목록** — 다만 표 머리글·소계·기호(`f`·`E`)·규격 구간
  (`10∼12`·`50이상`)·절번호는 자원이 아니므로 먼저 걷어냄. 안 걸렀을 때 1,550건이던
  목록이 **274건의 실제 공백**으로 줄어 쓸 수 있는 목록이 됨.

산출 — 매칭 **86행**(노무, 7직종: 보통인부 59·특별인부 19·조경공 4 등) ·
못 맞춘 **274건**(상위: 치즐소모량·자재·굴착기(무한궤도) 등).
⚠ **노임 카탈로그만 있어 노무만 맞음** — 기계·자재 카탈로그가 아직 없어 그 계열은
전부 못 맞춘 목록으로 감. 사유 문구에 그 사실을 적어 「이름이 틀림」과 구분함.

자체검증 — 신규 12건 포함 `pytest tmp/tests/ -q` **97 passed** · ruff 통과 · 409줄.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:36:59 +09:00
eomsangdonandClaude Opus 5 6e4cfc7dd2 feat(B08): 사면길이 유도·사면 4계열 면적 엔진
PLAN 8-4b 의 「사면적 없음」 구멍을 메움. 일감 3 의 엔진 덩어리.

⚠ B06 무접촉 — 저장분만으로 유도
  설계 엔진이 cut_slope_segments 를 내기는 하나 **정본에 저장되지 않음**
  (실측: route 150 측점 20.0 의 저장 design 키 33개에 그 키 없음. 저장되는 것은
  화면이 보낸 설계 지정이고 조회는 저장분을 그대로 실음). 그 값을 원천으로 쓰면
  사면적이 조용히 0 이 됨.
  대신 design_line + 저장된 경사비로 유도함 — 노체 끝 바깥으로 나아가며 기울기가
  설계 경사비와 맞는 연속 구간이 사면이고, 원지반은 경사비가 안 맞아 저절로 끊김.
  성토도 같은 원리로 잡힘(원지반선이 없어도 됨). 2단 사면(암/토사)은 경사비가
  달라 그대로 갈림.

⚠ 측구를 사면으로 세지 않음 — 개발 중 실제로 났던 결함
  측구 벽 경사가 n=1.0 으로 토사 절토비와 같아 그대로 걸렸음. 사면 시작점을
  노체 끝이 아니라 **측구 바깥 끝**(road_edges ± ditch.top_width_m)으로 옮겨 고침.
  그 형상을 실측 그대로 시험에 세워 재발을 막음.

사면 4계열 x 성토면/절토면 = (거리, 면적) 7쌍
  층따기[성토면만] · 면고르기 · 법면보호공 · 지장목제거.
  거리 = 그 측점 사면길이, 면적 = 토적표와 **같은 평균단면적법**(계산을 두 벌로 안 짬).
  법면보호공은 면고르기를 참조하되 끊을 수 있게 둠 — 실무 시트가 값이 같았던 것은
  엑셀 복사였고 오솔길 산출에는 보호공 4열이 0 이었음.

반영률은 법정값 아님 — 기본 100 %, 설계자 입력(PLAN 8-11 · 8-10 법대로).
  TODO(미결) — 실무 관측 80/50/80 중 지장목제거는 밑수가 안 맞아(14,061 != 10,782)
  쫓지 않고 100 % 로 둠.

잘린 측점을 감추지 않음 — slope_unclosed 를 물고 와 unclosed_stations 목록으로 냄.
  사면이 원지반을 못 만나면 사면길이도 잘려 있으므로 화면이 드러내야 함.

검증 — tmp/tests/test_b08_slope_area.py 15건 통과.
  실측 설계선을 그대로 세워 측구 제외·2단 암토사 분리·성토 유도·원지반 끊김을 못 박음.
  실데이터(route 150, 65측점) — 면적이 있는데 사면길이가 0 인 경우 0건,
  잘린 측점 17곳 검출.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:31:35 +09:00
eomsangdon 7d919c423d Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-07 20:30:59 +09:00
eomsangdon 123599db1a Merge remote-tracking branch 'origin/main_desktop_1' into main_laptop_1 2026-09-07 20:30:59 +09:00
eomsangdon 4d56f00acf Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-07 20:30:38 +09:00
eomsangdon af47523fc9 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-07 20:30:37 +09:00
eomsangdon 690d6b418c auto: 2026-09-07 20:29 (ESD_LAPTOP) 2026-09-07 20:29:26 +09:00
eomsangdonandClaude Opus 5 33aa07f782 docs(B06): cut_slope_segments 는 지금 읽는 곳이 없음을 코드에 명시
데스크탑 창이 「법정 경사 검사가 빈 값을 볼 수 있다」고 알려 와 확인함. **검사 자체가 없어
문제 없음** — 별표2 검사는 오늘 폐기됐고(`cc428d7e`, 암질을 횡단도에서 안 고르기로 사용자
확정), 지금 이 값을 읽는 코드는 **한 곳도 없음**(계산해 내보내기만 함).

같이 확인한 사실 — 저장분에도 안 들어감. 실측(공용 DB) `cross_sections.data.design` **33키**에
`cut_slope_segments` 없음(같은 자리에 `cut_area_m2` 는 있음). 저장되는 것은 화면이 보낸
**설계 지정값**이라 엔진 출력 전체가 아님.

그런데 주석 셋이 아직 「법정 경사 검사가 읽는다」로 남아 있어 **다음 사람이 저장분에서 읽으면
된다고 오해**할 자리였음. 그 셋을 고쳐 ① 지금 읽는 곳 없음 ② 저장분에도 없음 ③ 남겨 두는
까닭은 소단 기하가 이 셈 위에 서 있어서 ④ **되살릴 때는 저장분에서 읽지 말고 계산해서 쓸 것**
을 적음. 코드는 한 줄도 안 바뀜(주석만).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:29:25 +09:00
eomsangdonandClaude Opus 5 76b302be3a feat(B09): 단가 계층·단수 자리·프로젝트 스냅샷 (PLAN 9-2·9-3·9-4)
**단수 처리를 「출력 위치」에 바인딩** (`B09_Estimation_Rounding.py`)
- `단수처리_규칙.md` §1 — 같은 수량×단가라도 **내역서 본체는 절사, 집계표는 반올림**.
  함수가 항목이 아니라 **표 종류**에 붙음. 자리를 안 대고 부르는 길을 안 둠(기본값 없음).
- ⚠ **집계표와 본체의 합이 어긋나는 것이 정상** — 「합계가 1원 틀린다」에 계산을 고치지
  않도록 `summary_vs_body_gap()` 으로 차이를 값으로 드러내고 화면 문구를 상수로 둠.
- 계산은 전정밀, 자르는 것은 표 그리는 자리에서. 값은 원문에서 복사 안 하고 문서를 가리킴.

**단가 계층 — 표를 세 벌 만들지 않음** (`B09_Estimation_PriceBook.py`)
- 「제목 + 상세」 한 쌍 + 종류 구분. 층은 `S 중기취득가 → X 시간당사용료 → B 일위대가 →
  D 단가산출`, 바닥은 `M 자재`·`L 노임`, `W 일식·견적`은 **단가 0**(무대처리, 8-7 ㉡).
- 금액은 어느 층이든 **재료·노무·경비 3분할**, 합계 = 셋의 합.
- **단가 슬롯 6개** — 번호 고정, **이름은 프로젝트 설정**. 기본 채택 6번(`JUKNM=6`).
  슬롯별 **근거 쪽수**(STC `PG_` 열) 자리도 둠.
- 안전장치: 채택 슬롯이 비면 **0 으로 안 때우고 멈춤** · 없는 코드는 `unmatched_codes()`
  로 **목록으로 냄** · 참조 순환 감지.

**프로젝트 스냅샷** (`B09_Estimation_Storage.py`)
- 채택 단가를 `<project_root>/B09_Estimation/v1/` 에 사본으로. 기준자료가 갱신돼도
  **옛 프로젝트 결과가 안 바뀜**. `dataset_version` 은 `dataset_id`+`effective_date`+
  `sha256` **세 쪽**. 사본이 바뀌면 지문 불일치로 멈춤. 차수(당초·1~3차) 자리 비워 둠.

자체검증 — 신규 13건 포함 `pytest tmp/tests/ -q` **85 passed** · ruff 통과 ·
파일 최대 534줄. 중기 실측값(`X00005` 96,829 = 노 55,700 + 재 18,001 + 경 23,128)으로
3분할 유지·층 쌓임 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:28:57 +09:00
eomsangdonandClaude Opus 5 a48770d11d fix(B09): 관급 입력은 「순자재대」 — 조달수수료 차감 옵션 제거
`단수처리_규칙.md` §3 원문을 읽다 **입력 의미를 잘못 잡은 것**을 찾음.
실무 서류의 「관급자재대 69,850,000」은 이미
`ROUNDUP(순자재대 69,474,220 + 조달수수료 375,160, −3)` 한 값인데,
그 합계를 순자재대 자리에 넣고 있었음. 그래서 안전관리비 관급항이 부풀었고,
그 어긋남을 「조달수수료 차감 옵션」으로 덮고 있었음.

- **입력을 순자재대·수수료로 나눠 넣는 것으로 정정.** 그러면 ㉮ 관급자재대 천원 올림
  ㉯ 안전관리비 관급항 `순자재대 ÷ 1.1` 이 **둘 다 저절로 맞음**.
- ⇒ `deduct_procurement_fee_for_safety` **옵션 제거**(엔진·법정경비·라우터).
  8-10 의 「차감은 규정 문구가 아님」과 결론은 같되 이유가 더 단순함 —
  **애초에 수수료는 관급금액에 안 들어감.**
- 잘못 넣는 사고를 테스트로 박음 — `test_owner_supplied_input_is_net_material_not_total`
  (합계를 넣으면 안전관리비가 8,629 원 = 375,160 ÷ 1.1 × 2.53 % 커짐).
- 필드 주석에 「실무 서류의 관급자재대를 그대로 넣지 말 것」 경고 추가.

자체검증 — **관행 옵션 없이 기본값만으로** 두 벌 재현:
2024 요율 → 안전 16,586,996 · 총공사비 1,201,879,000 / 현행 요율 → 안전 **18,586,091**.
화면 실측(5174) — 안전관리비 A 18,586,091 채택 · 관급자재대 69,850,000.
pytest 72 passed · ruff 통과 · tsc 통과 · 백엔드 재시작 후 200.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:23:16 +09:00
eomsangdonandClaude Opus 5 9575ce230f merge: sub_desktop_1 되받기 — main.py 라우터 등록 충돌 해소
B08 토적표 라우터와 B09 원가계산 라우터가 main.py 의 같은 자리(import 블록·
include_router 목록)를 각자 한 줄씩 늘려 충돌함. 둘 다 필요한 줄이라 양쪽을
그대로 살림 — 어느 쪽도 버리지 않음.

확인 — 충돌 표시 0건, 앱 import 성공, 두 라우터 경로가 모두 등록됨
(quantity 3건 · estimation 3건).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:19:09 +09:00
eomsangdon aa3bfe76ba Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-07 20:17:54 +09:00
eomsangdonandClaude Opus 5 25f8da652d feat(B08): 토적표 화면 — 실무 3단 머리글 그리드
PLAN 8-4b 열 명세를 화면에 세움. 일감 2 의 마지막 덩어리(엔진 → API → 화면).

왜 실무 서식 그대로인가
  이 화면의 첫 사용자는 「프로그램이 맞나」를 확인하려는 설계자임. 보기 좋게
  재배치하면 실무 산출서와 눈으로 대조를 못 함. 열 순서·머리글 문구를 실무
  토적표(= 오솔길 1.BOM 36열)에 맞춤.

소수 자리는 표기 규칙일 뿐 (PLAN 8-16)
  서버는 전정밀 값을 주고 자르는 것은 화면뿐임. 실무 시트 관측대로
  단면적·체적 2자리 · 보정량계·유용토·차인·누가 1자리 · 거리 정수.
  원가 쪽(줄마다 원 단위 절사)과 규칙이 반대라 그 코드를 옮기지 말 것.

구성
  좌측 = 산출 조건 읽기 전용(산출법·토량환산계수). 계수는 서버 상수가 유일
  정의처라 화면이 값을 다시 적지 않음.
  우측 = 시트 탭 + 표. 지금 서는 장은 토적표 하나, 나머지 6장은 차례로 붙임.
  표는 한 번만 받아 좌측·우측이 함께 씀(중복 호출 없음).
  측점 열·머리글은 sticky — 가로로 굴러도 어느 줄인지 보임.

locale 은 B08 키 8개만 추가 (다른 창과 겹치는 파일이라 기존 줄 무접촉).

검증 — 공용 브라우저 실화면.
  타입검사 오류 0. 탭 「토적표」, 3단 머리글 3행(9/5/14셀), 본문 65행 x 20열,
  합계행 표시. 둘째 줄이 API 값과 일치: NO.1 거리 20 · 절토토사 1.96/22.34/20.10
  · 암 2.54/25.43/29.24 · 측구 0.08/2.58/2.33 + 0.10/1.02/1.17 · 보정량계 52.8
  · 성토 4.82/48.79 · 유용 48.8 · 차인 4.0 · 누가 4.0.
  ⚠ 어두운 테마에서 머리글이 묻히던 것을 프로젝트 테마 변수로 바꿔 고침 —
  대비 실측 머리글 10.51:1 · 본문 11.32:1 (기준 4.5:1).
  조작으로 바꾼 화면은 사용자가 보던 주소로 되돌려 놓음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:17:42 +09:00
eomsangdonandClaude Opus 5 ec23f87f83 fix(B09): 산출근거 문구 괄호 정정 · 진행단계 라벨도 「원가계산」으로
화면 실측에서 드러난 표기 오류 셋 (공용 브라우저 5174 조작 확인):
- **이윤 밑수가 뒤바뀌어 읽힘** — `(순공사원가+일반관리비) − 재료비 × 15%` 로 나와
  재료비에만 15 %를 곱하는 것처럼 보였음. 밑수가 합·차면 괄호를 씌우도록 고쳐
  `((순공사원가+일반관리비) − 재료비) × 15.0%` 로 나옴.
- **안전관리비 B 산출근거에 밑수 설명 문장이 통째로 박혀** 식이 안 읽혔음.
  밑수 라벨을 「재료비+직접노무비 (관급 제외)」로 줄이고 ×1.2 는 대괄호로 감쌈.
- **채택 요약 줄에 요율이 한 번 더 붙던 것** 제거 — 산식은 A·B 줄에 이미 있음.
- `WF_Step_Estimation` 라벨도 「설계도서」→「원가계산」 (페이지명과 어긋나 있었음).
- `F402` 정리: 구간 지문 루프 변수가 `dataclasses.field` 를 가리던 것 이름 변경.

자체검증 — 브라우저 실측 29줄 렌더 · 요율 판(2026-04-13 / 지문 250b1e4d…) 표시 ·
안전관리비 A 채택/B 취소선 · 환경보전비 「미확정」 경고 표시 ·
목표 도급공사비 1,192,000,000 입력 시 필요 조정액 **394,456** 제안(적용은 안 함) ·
`(1,192,433,902 − 1,192,000,000) ÷ 1.1 = 394,456` 손검산 일치.
pytest 32 passed · ruff 통과 · tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:16:22 +09:00
eomsangdon 79275ed070 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 20:14:46 +09:00
eomsangdon 188b9be696 Merge remote-tracking branch 'origin/main_desktop_1' into main_laptop_1 2026-09-07 20:14:43 +09:00
eomsangdon e46b89df11 auto: 2026-09-07 20:14 (ESD_LAPTOP) 2026-09-07 20:14:11 +09:00
eomsangdonandClaude Opus 5 f659aec8a1 feat(B05): 평면(지도)에도 구간형 구조물을 띠로 — 종단과 같은 자료
사용자 지시(2026-09-07). 계획서 3-6 마지막 항목 — 산마루측구·도수로·옹벽처럼 **구간**으로
놓이는 시설이 종단에는 띠로 보이는데 **평면(배수유역도)에는 아무 표시가 없었음**.

**「부품이 없어 새로 짜야 함」이 사실이 아니었음** — 계획선 표본(`strengthSamples`)이 **1m 간격**
이라 **배열 인덱스가 곧 누가거리**임(`resampleRoute`). 구간 → 화면 선은 그 토막을 잘라 굵게
긋기만 하면 됨. 새 파일 하나(88줄)로 끝남.

- `B05_Profile_UI_Drainage_Spans.ts` — 띠 그리기(`drawRouteSpans`)와 자료 만들기
  (`routeSpansFromStructures`). 구간형만 띠가 되고, 색은 **레지스트리 표시색**을 그대로 씀.
- 그리는 자리 — **계획선 바로 위, 강도 색칠·마커 아래**. 굵기 7px·투명도 0.45 라 계획선을
  덮지 않음. 이름은 안 적음(종단에 이미 있고, 지도 글자는 눈금·유역 번호와 겹침).
- 목록이 바뀌면 따라옴 — `setStructures`·`setStructureTypes` 가 띠를 다시 넘김.
- 저장분에 노선보다 긴 구간이 남아 있어도 잘라서 그리고, 시작=끝인 구간도 한 칸은 보임.

⚠ 프로필 패널이 이미 700줄을 넘겨 있어(843) **매핑 로직은 띠 모듈로 뺐음** — 패널에는 3줄만
남김(827줄). 그 파일의 초과는 이전부터임.

시험 5건 — 부품 존재 · 범위 밖 구간 안전 · **쌓는 순서**(계획선 → 띠 → 강도 색칠) ·
종단과 같은 자료에서 뽑는지 · 목록 변화가 따라오는지.

⚠ 실화면 확인은 아직임 — 공용 브라우저가 소단 무한루프(별건, `80db17d4` 로 고침) 로 멈춰 있어
사용자 새로고침 대기 중임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:14:09 +09:00
eomsangdon fe92941d7b auto: 2026-09-07 20:12 (EOMSANGDON-HOME) 2026-09-07 20:12:02 +09:00
eomsangdonandClaude Opus 5 b48ab7047c feat(B08): 토적표 엔진·API — 평균단면적법 체적화
PLAN 8-4b 열 명세대로. B06 이 이미 낸 측점별 단면적을 다시 재지 않고
체적화만 함 — 새 수량을 낳지 않으므로 캐시·조작 경로 불필요(CLAUDE.md 5장).

열 구성 (실무 토적표 = 오솔길 1.BOM 36열과 1:1)
  측점·거리·절토[토사·암 각 단면적/입적/보정량]·측구터파기[토사·암 각 3칸]
  ·보정량계·성토[단면적/입적]·유용토·차인토량·누가토량.
  사면 계열(층따기·면고르기·법면보호공·지장목제거)은 사면길이가 아직 없어 일감 3.

계산 규칙
  체적 = (앞 단면적 + 현 단면적)/2 x 거리. 첫 측점은 앞이 없어 체적 없음.
  보정량 = 체적 x 다짐 환산계수 — 정의처는 EARTHWORK_CONVERSION_FACTORS 한 곳,
  여기서 값을 다시 적지 않음(토사 0.90 / 리핑암 1.15 / 발파암 1.30).
  값을 자르지 않음 — 품셈 1-2-2 는 표기 규칙이고 절사는 화면 몫(PLAN 8-16).
  원가 쪽(줄마다 원 단위 절사)과 규칙이 반대라 섞지 말 것.

TODO(미결 · PLAN 8-4b) — 설계가 측구를 토사·암으로 안 나눠 줌(ditch_area_m2 한 값).
  잠정으로 그 측점 절토 토사:암 면적비로 안분함. 측구는 절토부에 파므로 같은
  지반을 만난다는 것이 근거. 설계가 측구 지반을 따로 내면 _split_ditch 만 교체.

API — GET /api/projects/{id}/quantity/{route_id}/earthwork-table,
  경로 생략형은 워크플로 최신 노선으로. main.py 는 자기 두 줄만 추가.

검증 — tmp/tests/test_b08_earthwork_table.py 14건 통과.
  거창 실무 BOM 실측값 재현(단면적 1.89 → 체적 9.45 → 보정 8.505,
  다음 측점 18.10 → 16.29, 측구 0.18㎡ → 10m당 1.80 → 1.62).
  공용 브라우저에서 실 API 호출로 route 150·측점 65곳 확인 — 측점 20 에서
  (0.2723+1.9615)/2x20 = 22.338, x0.9 = 20.1042, 암 25.427 x1.15 = 29.24105,
  측구 안분 0.0784+0.1016 = 0.18 로 전건 일치.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:11:53 +09:00
eomsangdonandClaude Opus 5 2a21f99ba4 feat(B09): 원가계산 라우터·화면 신설, 페이지명 「원가계산」으로 변경
- `B09_Estimation_Router.py` 신설 — 무상태 계산 엔드포인트 3종.
  · `POST …/estimation/cost` 원가계산서 한 장(줄마다 비목·금액·요율·산출근거)
  · `GET …/estimation/items` 비목 정의 목록
  · `POST …/estimation/confirm` stage 6 완료 전이
  요율 구간을 못 고르면 500 이 아니라 **422 + 사유**로 알림 (기본값으로 안 때움).
  `target_contract_amount_krw` 를 주면 필요한 이윤 조정액을 **보여만 줌**(★법대로 8-10).
- `B09_Estimation_UI_Page.ts` — 셸에서 실제 화면으로. 3단 레이아웃, 좌측 입력 4군
  (공사조건·요율 판 읽기전용·관급자재·이윤 조정) + 우측 탭 8장(원가계산서 활성).
  원가계산서 표는 **네 칸 + 비고**이고 **안전관리비 A·B 두 줄이 나란히**, 채택 줄은
  강조·미채택 줄은 취소선.
- `main.py` — B09 라우터 import·등록 **자기 줄만** 추가.
- `ui_template_locale_b2.ts` — B09 키 추가, `B09_Estimation_Title` 을
  「설계도서」→**「원가계산」**으로 변경 (PLAN 9-1 사용자 승인).
- 자체검증: `tsc --noEmit` 통과 · 백엔드 재시작 후 `openapi.json` 에 3개 경로 등록 확인 ·
  `/api/health` 200. 화면 조작 검증은 다음 단계(브라우저 창이 닫혀 재기동 필요).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:11:15 +09:00
eomsangdonandClaude Opus 5 c044e33dab fix(B09): 규모 구간을 추정가격 반복 수렴으로 판정
- 근거 = 국가계약법 시행령 제7조 1호 「공사계약의 경우에는 **관급자재로 공급될 부분의
  가격을 제외한 금액**」. 우리 계산의 그 값은 **총원가**(부가세 전·관급 밖)임.
- 씨앗은 직접공사비로 두되 **거기서 멈추지 않음** — 나온 총원가로 구간을 다시 판정해
  구간 지문이 굳을 때까지 되풀이(최대 5회). 진동하면 **높은 쪽 구간 채택**하고
  결과 `notes` 에 남김. `estimated_price_krw` 명시 시 반복 없음.
- 구간 지문 = 일반관리비·이윤·고용보험·하도급보증 구간 + 적용 하한(환경·퇴직) 충족 여부.
- ⚠ 잠재 결함 하나 같이 고침 — 하도급보증 30억 이상 구간이 공종(토목·산업설비/건축)으로
  갈려 **구간 2개가 겹쳐 멈추던 자리**. `select_bracket(prefer_suffix=…)` 로 공종을
  부르는 쪽이 대게 함 (조용한 기본값 아님).
- 자체검증: 32 passed. 씨앗 47억(8 %) → 총원가 50억 초과 → **6.5 % 로 재판정**되는
  경계 넘기 사례를 테스트로 박음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:06:03 +09:00
eomsangdonandClaude Opus 5 50871e60b9 feat(B09): 원가계산 엔진 완성 — 법정경비 분리·이중계상 감시·재현 검산
- `B09_Estimation_Statutory.py` 신설(법정경비 14비목) — 700줄 제한 대비 분리.
  **비목 목록을 코드에 안 박음**: 그 해 요율 데이터에 변수가 있는 것만 유효(8-13·8-14).
- `B09_Estimation_Guards.py` 신설 — 이중계상 거울 테스트 3종을 함수로 둠(8-7 ㉠㉡㉢).
  할증 두 번 · 무대 줄 단가 · 배합 두 번 쪼개기를 **수치로 잡아 멈춤**.
- 엔진: 이윤 3줄(조정 전·조정액·조정 후) · 관급자재대 천원 올림 · 폐기물처리비 실비 슬롯 ·
  `formula_text`(줄마다 제 산식 — 실무 원문의 A식 복사 오류를 안 따라감) ·
  `proposed_profit_adjustment`(필요액을 보여만 주고 적용은 명시로, ★법대로 8-10).
- 요율 로더에 `load_rate_dataset_from_path` 추가 — 옛 연도 재현 검산 전용, 지문이 없어
  정본이 아님이 결과에 드러남.
- 검산 고정값 두 벌(`tmp/tests`, git 밖):
  · 2024 요율 → 울진 공통 금액 사슬 **전건 재현**(안전 16,586,996 · 이윤 108,109,955 ·
    총원가 1,029,117,273 · 총공사비 1,201,879,000)
  · 현행 요율 → 안전 18,586,091. **같은 입력·같은 코드, 요율만 교체** — 연도 교체 구조 증명
  · 거창 A/B min **양방향**(울진 A 채택 · 거창 B 채택)
- 자체검증: `pytest tmp/tests/ -q` **68 passed** · `ruff check` 통과 · 파일 최대 441줄.
- 잠정 반영(TODO 주석 + 계획서 항목번호): 환경보전비 임도 요율 0.9 % 잠정 ·
  폐기물처리비 자리 미확정 · 조달수수료 차감은 기본 꺼짐(옛 서류 재현용 옵션).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:01:08 +09:00
eomsangdonandClaude Opus 5 3497486bed feat(B08): 산림사업 표준품셈 476표 → 공종 마스터 정규화 (공종 축)
PLAN 8-5·8-6·8-7 의 B08 일감 1번. B09 의 ③단가산출을 여는 선행 작업임.

공종의 정체 = 품셈 절 번호
  「9-3-1. 인력」처럼 절 제목이 공종이고 표의 행은 조건별 변형(토질·암종·규격)임.
  계층·정렬은 목차표(F0001)에서 나오고 표가 그 절에 붙음. 목차 477노드,
  표 456/475 귀속(미귀속 19 는 절번호 없는 부록·경과조치).

pum_form — 뒤집히면 값이 조용히 반대가 되는 자리
  productivity(작업능력, 품=1÷값) 16 · requirement(소요량, 품=값÷밑수) 244 ·
  coefficient(시공능력 공식 K·f·E) 19 · reference(1장 적용기준·할증·참조지시) 94 ·
  undetermined 83.
  ⚠ 판정 순서가 뜻을 가짐 — 「작업능력(㎥/hr)」 표의 비고에 「보통인부 1인/일」이
  흔히 붙어 있어 직종을 먼저 보면 생산량형이 소요량형으로 뒤집힘. 생산량형 표지를
  직종보다 앞에 둠.

담당 경계 (PLAN 8-7)
  공종 축만 만들고 자원 축(resource_kind·resource_code·amount)은 안 채움 — B09 몫.
  대신 원문 셀 raw_row 를 그대로 실어 B09 가 476표를 다시 열지 않게 함.
  판정 실패분은 빈칸이 아니라 form_undetermined_*.json 목록으로 냄.

산출 (resources/data_work_item_master/)
  work_item_master_2026-01-01.json · form_undetermined_2026-01-01.json · _manifest.json.
  dataset_version 에 dataset_id·effective_date·sha256 세 쪽 기록 — 재현성.
  공종코드 FP-09-03-01 형식, sort_order 는 STmate 관례대로 256 간격.

검증 — tmp/tests/test_b08_work_item_master.py 14건 전건 통과.
  생산량형이 비고의 직종에 뒤집히지 않는 것, 직종이 둘째 열에 있어도 잡는 것,
  raw_row 보존, 자원 축 미혼입, 미판정의 목록 등재를 각각 못 박음.
  전체 회귀 382 passed (실패 3건은 B05 구조물·코리도 기존 깨짐, 본 변경과 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:01:02 +09:00
eomsangdon bf895fb056 Merge remote-tracking branch 'origin/sub_desktop_1' into sub_laptop_1 2026-09-07 19:58:20 +09:00
eomsangdon ee16ee21d9 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 19:58:19 +09:00
eomsangdonandClaude Opus 5 80db17d463 fix(소단): 제자리 무릎이 되풀이돼 화면이 멈추던 것 — 앞으로 갈 때만 무릎 인정
**실사고(2026-09-07)** — 사용자 공용 브라우저에서 **소단을 한 건 놓자 화면이 25분 넘게 멈춤**
(렌더러가 1.4코어를 계속 태움). 서버(파이썬)로 같은 측점을 돌리면 0.01초에 끝나 **서버는
멀쩡**했고 화면만 물렸음.

**원인** — `cut_profile_points` 의 무릎(암 경계 교차) 가지에서 **제자리 뒤집기**.
암 경계선의 기울기가 **암 경사와 토사 경사 사이**면
· 토사로 바꾸면 다음 걸음이 경계 **아래** → 「다시 암」
· 암으로 바꾸면 다음 걸음이 경계 **위** → 「다시 토사」
가 되고, 이때 보간 비율 `share` 가 0 이라 `knee_dist == dist` — **한 걸음도 안 나감.**
`while dist < limit` 이 끝나지 않음. `multi_knee` 는 **소단이 있으면 항상 참**이라 소단이 지뢰였음.

**고침 둘**(파이썬·TS **같은 모양**)
1. **앞으로 나아가는 무릎만 인정** — 직전 무릎 자리를 들고 있다가 같은 자리(1e-9 이내)면
   뒤집지 않고 한 걸음 감. **첫 무릎은 종전대로 인정**되므로 `multi_knee=False`(소단 없는
   경로)는 한 비트도 안 바뀜 — 그쪽은 첫 뒤집기 뒤 가지 자체가 닫힘.
2. **걸음 수 상한 40,000**(정상 최대 4,000의 10배). 넘으면 **조용히 자르지 않고 경고**를 남김 —
   조용히 자르면 절토선이 짧아진 채 값이 나가 또 조용히 틀림(25 지적).

**시험 5건**(`tmp/tests/test_berm_knee_progress.py`) — 경계 기울기를 두 경사 사이에 둔 재현으로
① 8초 안에 끝나는지 ② 거리가 뒤로 안 가는지 ③ 소단 없는 경로가 종전 그대로인지
④ 두 파일이 같은 규칙을 쓰는지 ⑤ **TS 를 실제로 돌려 파이썬과 꼭짓점 수·끝점이 같은지**.
고치기 전에는 ①이 무한 대기였음. 전체 **491 passed · 17 skipped**, `tsc --noEmit` 통과.

버그를 낸 창(25)과 확인하고 진행함 — 겹치는 작업 없음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:57:44 +09:00
eomsangdon 128a40d8f0 Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-07 19:49:14 +09:00
eomsangdon 0799e733ab Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-07 19:49:12 +09:00
eomsangdon 197b00aef1 Merge remote-tracking branch 'origin/main_desktop_1' into sub_laptop_1 2026-09-07 19:49:10 +09:00
eomsangdon 9406551544 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 19:49:01 +09:00
eomsangdonandClaude Opus 5 8b7325a28e feat(B09): 원가계산 엔진·요율 로더 뼈대 추가
- `B09_Estimation_Rates.py` — 요율 데이터(`resources/data_cost_input_value/rates_*.json`)
  로더 + 구간 조회. 요율을 코드에 안 박음. 구간 라벨의 `billion` = 십억 원.
  구간 판정 실패 시 기본값으로 안 때우고 `RateLookupError` 로 멈춤.
- `B09_Estimation_Engine_Cost.py` — ⑤ 공사원가계산서 계산. 순공사비를 받아
  법정경비·일반관리비·이윤·부가세를 얹음. 수량·단가와 무관하게 홀로 돎.
- 지킨 것 (PLAN 8-9·8-10, 실무 원가계산서 재현으로 확인된 것만)
  · 모든 줄 원 단위 버림 · 밑수가 항목마다 갈림(직노 / 직노+간노 / 건강보험료 / …)
  · 안전관리비 A·B 두 값 중 작은 쪽 — 대상액이 구간 경계를 넘으면 A 가 더 커짐
  · 이윤 수동 조정액은 설계자 명시 입력일 때만 · 비목 목록을 코드에 안 박음
- 검사 16건 별도(`tmp/tests/test_b09_cost_engine.py`, git 밖) 전건 통과.
  실무 실측 두 방향(울진 A 채택 · 거창 B 채택)을 고정값으로 씀.
- 기존 파일 무수정 · DB 미사용(기준자료는 파일, PLAN 8-5·9-2).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:49:00 +09:00
eomsangdon acb775dc4e Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-07 19:47:56 +09:00
eomsangdon 26ecf8f20b Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 19:47:47 +09:00
eomsangdonandClaude Opus 5 a6e0e9888d feat(B05): 노선 편집 패널 — 화면으로 띄우기, 16방위 자동 위치, 손으로 옮기기, R·길이 고정
사용자 지시 5건. ⚠ **화면 검증은 아직 못 함** — 다른 창의 분배 조율로 git sync 를 먼저
하게 되어 미커밋으로 두지 않으려고 먼저 커밋함. 타입 검사·시험은 통과 상태임.

- **1. 패널을 화면으로** — `document.body` 에 `position: fixed` 로 띄움. 모달이
  `overflow: hidden` 이라 안에 두면 가장자리에서 잘렸음. 이제 모달 밖으로도 넘어감.
- **2. 너무 가까움 + 위치 제어** — 여백 14 → 40px 로 넓히고 방위별 상자 반지름만큼 더 밀어냄.
  **머리를 잡아 손으로 옮길 수 있음**. 옮긴 자리는 꺾임점 기준으로 기억해 지도를 옮기거나
  확대해도 따라오고, 다른 꺾임점을 고르면 자동 자리로 돌아감.
- **3. 자동 위치를 16방위로** — 4방위는 대각 자리에서 곡선을 물었음. 곡선 중심의 반대 방향을
  22.5° 단위로 맞춤.
- **4. 「법정 하한」 삭제**, 「칸을 비우면 자동」을 **다음 줄**로 뺌. 대신 무엇을 붙들고
  있는지(고정 없음 / 반지름 고정 / 곡선 길이 고정)를 그 줄에 냄.
- **5. 반지름·곡선 길이 고정 단추** — 노드를 옮기면 교각이 바뀌어 R 과 길이 중 하나는 반드시
  따라 움직임. 그래서 **셋 중 하나**임: 자동 / R 고정(길이가 따라감) / 길이 고정(R 을 L/Δ 로
  다시 잡음). 붙들어 둔 곡선은 **손잡이로도 안 바뀜**(끌면 R 이 바뀌므로 집기에서 제외).
  잠금·길이는 되돌리기 사진에도 담김.
- 새 모듈 `_Edits.ts` — 잠금 반영·상태줄 요약·서버 응답 펴기. 본체 692줄로 700줄 유지.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:47:39 +09:00
eomsangdon b543482e9e Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 19:47:10 +09:00
eomsangdon 08d480f2b1 Merge remote-tracking branch 'origin/sub_laptop_1' into main_desktop_1 2026-09-07 19:44:05 +09:00
eomsangdonandClaude Opus 5 4300be8385 fix(B05): 노선 편집 접선점을 늘 보이게, 라벨을 R+곡선 길이로, 중심 반대쪽에 배치
사용자 지적 3건 반영.

- **접선점 표기 복구** — 손잡이를 「고른 곡선만」으로 줄이면서 직선↔R 만나는 자리의
  **표기까지 없앴음**. 접선점은 손잡이이기 이전에 읽을 정보라 **늘 그림**(고른 곡선은
  속을 채워 도드라지게). 노드를 못 집던 문제는 집기 우선순위로 이미 풀려 겹치지 않음.
- **라벨을 반지름 + 곡선 길이 두 칸으로, [자동] 단추 삭제** — 교각 Δ 는 앞뒤 직선이
  정하므로 L = R·Δ 로 묶임. 길이를 받으면 R 로 바꿔 한 값만 보관. 칸을 비우면 자동.
- **라벨 자리를 곡선 중심의 반대쪽으로**(상하좌우) — 중심 쪽에 두면 곡선을 가림.
  중심 방향은 접선점 두 방향 단위벡터의 합(각 이등분선)으로 구함.
- ⚠ 라벨 글자가 안 읽히던 것 — `--color-surface-2` 가 이 테마에 없어 밝은 기본값으로
  떨어졌음. 모달 본체와 같은 토큰으로 교체(실측 배경 rgb(37,31,56)·글자 rgb(228,224,240)).
- 상자 높이가 늦게 자라 자리가 11px 어긋나던 것도 다음 프레임에 다시 맞추게 함.
- 700줄 유지 — 교각·중심방향은 `_Label`, 등고선 띠는 `_Input` 으로 옮겨 본체 687줄.

실화면 검증 — 접선점이 고르기 전에도 보임 / 곡선 길이 40 → 반지름 203.7m 로 따라옴 /
라벨이 중심 반대쪽(위)에 붙음 / [자동] 없음. 554 passed · 18 skipped, tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:28:02 +09:00
eomsangdonandClaude Opus 5 f84b3d9451 feat(B05): 노선 편집 — R 라벨을 고른 자리 옆으로, 되돌리기·다시하기·초기화, 줌·팬 통일
사용자 지적 ③④⑤⑥ 과 추가 지시(되돌리기)를 한 묶음으로 처리.

- **③ R 라벨** — 모달 맨 아랫줄이던 곡선 편집칸을 걷고, 고른 꺾임점 **옆에 뜨는 라벨**로
  바꿈(`_Label.ts` 신설). 캔버스 밖으로 나가면 반대쪽으로 접어 넣음. 확대·이동·창 크기가
  바뀌어도 고른 노드를 따라감.
- **④ 노드가 손잡이보다 먼저 잡히게** — 반대였던 탓에 헤어핀처럼 곡선이 몰린 데서는
  노드를 아예 못 집었음. 손잡이는 **고른 곡선에만** 그림. 머리말 안내에 조작 여섯을 적음.
- **⑤ 줌·팬을 배수유역도와 동일하게**(`_Input.ts` 신설) — 휠은 **당기면 확대**(반대였음),
  계수 1.15/0.87, 상한은 화면 폭 16m 기준, 하한 0.5, **팬은 가운데 버튼 전용**.
  ⚠ 커서 고정 계산이 좌상단 기준이라 확대할수록 지점이 밀리던 것도 중심 기준으로 고침.
- **⑥ 등고선을 노선 ±300m 띠로** — 창 크기와 무관한 고정 띠라 창을 늘려도 안 깨짐.
  화면 밖 걸러내기는 종전대로 `drawPreparedLayer` 가 함.
- **되돌리기·다시하기·초기화**(`_History.ts` 신설) — [확인]이 무거워 되돌릴 길이 없으므로
  창 안에서 물릴 수 있게 함. **끌기 한 번이 한 걸음**이고 클릭만으로는 걸음이 안 생김.
  [초기화]는 **이 창을 연 상태**로 (≠ [예상노선으로]). Ctrl+Z / Ctrl+Y · Ctrl+Shift+Z.
- 700줄 제한 — 집기(hit test)를 `_Input.ts` 로 옮겨 본체 695줄.

실화면 검증(5174) — 라벨이 노드 오른쪽 [+18,−38]px 에 뜸 / 휠 당기니 중심에서 373.1 →
428.1px 로 확대 / 왼쪽 끌기로는 지도 안 움직임 / 가운데 버튼 끌기는 [60,40] 그대로 따라옴 /
단추 켜짐이 여섯 단계 모두 맞음. 전체 554 passed · 18 skipped, tsc 통과. 정본 안 건드림.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:10:07 +09:00
eomsangdonandClaude Opus 5 d03b46e22e chore(db): 안 쓰는 빈 표 audit_logs 삭제 — 정본은 system_audit_logs
사용자 지시(2026-09-07 「묵은 것 청소」). 이름이 비슷한 표가 둘이라 **「감사 기록이 비었다」는
오진이 실제로 한 번 났던** 자리임.

지우기 전 확인(공용 DB) — `audit_logs` **0행** · 정본 `system_audit_logs` **81행** ·
이 표를 참조하는 **외래키 0건** · 코드는 쓰기(`common_util_audit.py:61`) · 읽기
(`B01_Dashboard_Repository.py:455·460`) · 정리(`purge_expired_audit_logs`) 전부 정본만 씀.

최초 스키마(`001_create_schema.sql`)는 **안 고침** — 지나간 이력은 다시 쓰지 않는 것이 규칙임.
새로 설치하면 001 이 만들고 이 파일이 지움.

공용 DB 라 **적용 전 두 창(보조 워크트리 · 데스크톱)에 알리고** 「막을 것 없음」을 받은 뒤 돌림.
서버 재시작 없음. 적용 후 확인 — 표 사라짐, 정본 81행 그대로.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:07:53 +09:00
eomsangdonandClaude Opus 5 10b4b32a85 feat(B05): 노선 편집 곡선을 브라우저가 즉시 그리게 — 손대면 곡선이 사라지던 것
사용자 지적 ①② 의 뿌리 하나를 고침. 곡선을 서버만 그려서, 노드를 끌거나 손잡이를
살짝 건드리기만 해도 그려 둔 선·손잡이·노드 요약을 통째로 비웠음. 그 결과 화면에서
곡선이 전부 사라져 「R 이 지워졌다」로 보였음(값 자체는 남아 [확인] 때 반영됐음).

- `buildEditedPolyline` 신설 — `build_planned_polyline` 의 **편집 갈래**
  (simplify=False + curve_flags/radii) 파이썬·TS 짝. 단순화·IP 추출·반지름 피팅은
  초기 변환 전용이라 안 옮김.
- `markEdited` 가 비우는 대신 **같은 규칙으로 다시 그림**. 나머지 곡선은 안 사라짐.
- 손잡이는 곡선 목록 자리가 아니라 **노드 번호**로 잡음 — 다시 그릴 때마다 목록이
  새로 나므로 자리로 들면 엉뚱한 곡선을 가리킴.
- 접선 자리가 모자라 R 이 눌리는 것이 끄는 즉시 보임(상태줄 「기준 미달 N곳」).
- 끌기 중에도 상태줄을 갱신 — 예전에는 아무 말이 없어 사라진 인상만 남았음.
- 서버가 곡선을 안 둔 자리(내각 179° 이상)는 **곡선 없음으로 열게** 고침. 전부 켬으로
  열면 아무것도 안 만지고 [확인]만 눌러도 곡선이 새로 생겨 노선이 조용히 바뀌었음.

거울 시험 `tmp/tests/test_route_polyline_browser_mirror.py` 10건 추가.
원호 표본 개수는 `math.sin` 의 마지막 자리 차이로 90° 처럼 딱 떨어지는 자리에서 하나
갈릴 수 있어(같은 원 위의 같은 호), 개수가 같을 때는 1e-9 로 자리까지, 갈릴 때는
현 하나분 안쪽으로 모양을 맞춤. 일부러 공식을 틀어 시험이 잡는 것도 확인함.

실화면(5174) — 노드/손잡이를 끈 뒤에도 상태줄이 「곡선 22곳(하한 R 12m)」 유지,
「기준 미달 2곳」이 그 자리에서 뜸. 종전에는 「곡선 기준 R 12m」로 바뀌며 다 사라졌음.
전체 554 passed · 18 skipped, tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 18:50:42 +09:00
eomsangdonandClaude Opus 5 4774eb3efb docs(소단): 모듈 설명 첫 줄의 기본 기울기 2° → 0° 정정
아래 항목 설명은 0° 로 고쳐져 있었는데 머리말만 2° 로 남아 서로 어긋났음.
값 자체는 이미 0 이라 동작 변화 없음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 18:16:10 +09:00
eomsangdon 29559b2b5e Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1
# Conflicts:
#	B06_Section/B06_Section_Api_Types.ts
2026-09-07 18:09:36 +09:00
eomsangdon 77a614ecf9 Merge remote-tracking branch 'origin/main_desktop_1' into sub_laptop_1 2026-09-07 18:08:34 +09:00
eomsangdonandClaude Opus 5 674d4ef14a fix(토공): 도자 운반 한계거리 70m → 60m 확정
근거 — 실무 오솔길 EARTH.DAT 헤더 6개 공사지가 전부 `20.0 / 60.0`
(거창·장수·진안·봉화·영월 본선·지선). 산림과임업기술 5장 「다. 공사수량의
산출」도 「도저운반성토 60m 이하 / 덤프운반성토 60m 초과(건설표준품셈
참조)」로 규정. 70m 는 Aislo 단독값이었음(2026-08-02 잠정 확정).

- config_system_design.py — EARTHWORK_HAUL_EQUIPMENT_LIMITS_M 의 dozer
  경계와 주석의 확정 이력·근거 갱신. 정의처가 이 상수 한 곳이라 다른 코드
  변경 없음.
- common_util_mass_haul_balance.ts — 경계현 설명 주석의 70m 표기 정정.

검증 — 백엔드 재시작 뒤 공용 브라우저에서 실제 API 호출,
sections/context 의 dozer.max_distance_m = 60 확인. 회귀 369 passed
(실패 3건은 haul 미참조 기존 깨짐).

지식DB 미결 No.21 해소 — 목록 정리는 위키 AI 몫.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 18:08:18 +09:00
eomsangdonandClaude Opus 5 e7695c678f feat(B06): 지반유형을 「토사」 토글 하나로 — 끄면 암(기본), 표준 패널에 각도 병기
사용자 확정(2026-09-07): 「리핑암과 발파암 버튼을 삭제하면서 구분의 의미가 없어졌어. …
토사버튼만 존재하고 이값은 활성화/비활성화로 반영(기본값은 비활성화) / 비활성화 상태에서는
암경계선이 나오고 암경계선 아래는 사용자가 지정한 암 절토각 반영. 이후는 토사 절토 각도를
반영 / 활성화 상태에서는 암반이 없으니 절토는 토사 절토 각도로만 구현」

**화면** — 카드 제목줄의 지반유형 버튼 셋(토사·리핑암·발파암)이 **「토사」 단추 하나**가 됨.
색으로 켜짐/꺼짐을 보이고 **기본은 꺼짐(암)**. 측구 토글과 같은 꼴임.

실화면 확인(용화 5601e828 · route 169 · 측점 1+0.0):
· **암**(꺼짐) — 암 경계선·암 절토각 칸 보임, 절토 토사 1.99 + 암 1.91 = **3.90㎡**
· **토사**(켜짐) — 두 칸 사라짐, 절토 **전량 토사 7.20㎡**(토사 경사가 더 완만해 더 팜)
· 다시 끄면 3.90㎡ 로 정확히 원복

**저장값은 종전 그대로** — 꺼짐 = `ripping_rock`, 켜짐 = `soil`. 옛 자료의 `blasting_rock`
도 「암」으로 읽힘. ⚠ 유토곡선·수량은 이제 암을 **한 종류(리핑암)**로 잡음 — 연암:경암 ·
발파암:리핑암을 가르는 것은 **설계내역에서 설계자가 비율로**(계획서 8-1).

표준 횡단면 설정의 절토·성토 경사 칸에 **각도를 함께 보임**(툴팁) — 카드가 도(°)로 받으므로
두 자리의 말이 갈리지 않게 함. 1:0.4 = 68.2° · 1:1.0 = 45° · 1:1.5 = 33.7°.

⑤ **표준을 바꿔도 개별로 고친 측점은 그대로**를 실화면에서 확인 —
1번 카드에 40° 를 넣고 표준 암 절토경사를 0.4 → 0.8 로 바꿔 [전체 측점 반영]:
· 1번(사용자 값) **40.0° 유지 · 암 절토 5.66㎡ 그대로**
· 2번(값 없음) 68.2° → **51.3°**(=1:0.8) · 암 절토 2.61 → 4.01㎡
· 표준을 되돌리자 둘 다 원상 복귀

시험 3건 더 — 토글 하나인지 · 기본이 암인지 · 옛 발파암 자료도 암 기하로 읽히는지.
전체 **486 passed · 17 skipped**.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 18:05:46 +09:00
eomsangdonandClaude Opus 5 6b9c115baa feat(B06): 절토 경사를 사용자가 넣게 — 카드에 암 절토각(°), 전체는 표준 설정
사용자 지시(2026-09-07): 「대신 경고부분은 삭제해주고 대신 각도를 사용자가 넣을수 있게 반영.
전체 공통으로 변경하는 경우에는 기본값 지정으로 하면 되지만 횡단도 하나만 변경하는 폼은
가져야함 / 개별 횡단도에는 암 절토 각도의 개별 수정 가능해야함」

**화면** — 암 측점 카드 아래에 「암 절토 68.2° ↺」 칸이 섬. 값을 넣으면 그 측점만 사면이
새 경사로 다시 그려지고 절토량도 따라 바뀜. ↺ 는 표준값으로 되돌림. 전체를 바꾸는 자리는
종전대로 좌측 [표준 횡단면 설정]임.

**계산에 넣는 자리는 한 곳씩** — 파이썬 `compute_cross_design(cut_slope_ratio=…)` ·
TS `computeCrossDesign({cutSlopeRatio})` 의 **그룹을 만든 바로 뒤**에서 경사비만 갈아 끼움.
부르는 쪽 9곳에서 표준값을 측점마다 복제하는 방식은 안 씀(한 곳만 빠져도 값이 조용히
사라지는 실패군). 기하·소단 코드는 한 줄도 안 건드림 — 25 확인대로 소단이 그 값을 읽어
서므로 **위치·개수가 새 경사를 저절로 따라옴**.

⚠ **경사는 나르는 값이 아니라 기하 입력임** — 계산 뒤에 키만 베껴 붙이면 설계선은 옛 경사로
그려지고 숫자만 새것이 됨(소단에서 겪은 자리). 그래서 재계산 세 경로(포장 강제·세월교 하강·
선형 재계산)와 브라우저 재계산·서버 프리뷰 **모두 계산 인자로** 넘김.

**되돌리기는 0 을 남김** — 세션에서 지우기만 하면 정본에 남은 옛 사용자 값이 되살아나
표준으로 못 돌아감. 0 = 「표준값을 씀」.

값의 길 — 세션 `cutslope`(등록표 한 줄) → 카드 입력 → [저장]·[확정]에서 `cross_patches`
(`design.cut_slope_ratio_user`) → 정본. `USER_TOUCHED_KEYS` 양쪽에 넣어 **표준을 바꿔도
개별로 고친 측점은 그대로** 둠(사용자 원문 끝줄).

시험 — 새 7건(넣은 경사가 실제로 그려짐 · 무릎 위 토사 경사는 그대로 · 0 은 되돌림 ·
재계산에도 남음 · **계산 전에 넘어감** · 각도↔경사비 · 칸은 암 측점에만),
거울 시험에 「암 절토 경사를 측점에서 바꿈」 한 갈래 추가, 7-3 회귀 한 줄 추가.
전체 **483 passed · 17 skipped**.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 17:52:06 +09:00
eomsangdonandClaude Opus 5 7833b9e677 feat(구조물): 소단을 「구조물 배치」로 옮김 — C군 사면안정 신설 (계획서 3-9, 사용자 재확정)
사용자 확정(2026-09-07) — 소단은 좌측 별도 폼이 아니라 **다른 옹벽·기슭막이와 같은 자리**
(구조물 배치)에서 놓는 것으로 바뀜. 기하는 그대로 두고 **입구만 옮겼음.**

· 레지스트리에 **C군(사면안정) 「소단」** 신설. 옵션은 다른 C군 구간형과 같은 꼴 —
  길이·기준측점 전·후 + 폭·간격(사면길이)·안쪽 기울기.
· ⚠ **소단은 C군 구간형이지만 벽이 아님** — 기슭막이 제원 자리에 얹히지 않게 파이썬·TS
  양쪽 벽 필터에서 뺐음. 안 뺐으면 소단을 놓는 순간 「독립 기슭막이」로 그려졌을 것임.
· 구조물 목록이 바뀌면 소단 구간을 세션 사본(`berm`)으로 펴고, 달라졌을 때만 재계산함.
  계산 계통(브라우저·서버·`USER_TOUCHED_KEYS`·옹벽 의무 판정)은 **한 줄도 안 바꿨음**.
· ④에서 만든 좌측 별도 「소단」 패널은 걷어냈음(모듈 삭제).

**안쪽 기울기 기본값 2° → 0°** (사용자 재확정). 기울이는 것은 실무이나 법령·교본 근거가
없어 기본으로 넣지 않고 폼에서 받음. 주석·지식DB 방침도 그에 맞춰 고침.

**소단측구**(B군 종단배수)도 같은 꼴로 옵션을 붙였음 — 소단 위에 놓이는 시설이라 폭·간격이
소단을 따르고, 기울기만 **사면 쪽**(소단과 반대). 소단이 실제로 서면서
`PENDING_TYPE_IDS` 빈 칸이 풀려 연장 수량이 다른 구조물과 같은 길로 나옴.

자체검증 — 「근거 없는 기본값 금지」 시험에 두 타입 12개 기본값을 근거와 함께 등재.
소단측구 연장 시험을 「빈 칸」에서 「연장 100m」로 뒤집음. 전체 544 passed · 18 skipped.
TS 타입 검사·ruff 통과. 화면 확인은 다음 단계.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 17:29:50 +09:00
eomsangdonandClaude Opus 5 c7770b7529 fix(횡단): 옹벽 의무 판정이 소단을 실제로 보게 함 — 부호·쪼개짐 두 곳 (계획서 3-9 ⑥)
앞 커밋으로는 화면 값이 안 바뀌어 실화면에서 두 가지를 더 찾았음.

① **TS 결과에 소단을 안 실었음** — 파이썬만 되싣고 브라우저 판을 빠뜨렸음.
   판정(`fillSlopeLengths`)은 브라우저에서 도는데 `design.berm` 이 없어 종전 식으로 갔음.

② **소단을 폭으로 찾으면 못 찾음** — 설계선 꼭짓점에는 지반 샘플(0.5m 격자)이 섞여 있어
   폭 0.5m 짜리 소단이 **두 도막으로 쪼개짐**(실측: 평탄부 0개). 게다가 설계선은 오프셋
   오름차순이라 **우측(음수) 성토면은 같은 소단이 `-기울기`로 나와** 부호를 그대로 대면
   역시 못 찾음.
   ⇒ **기울기로 가름.** 소단은 2°(0.035), 성토는 1:1.2~2.0(0.5~0.83)이라 성토 기울기의
   절반만 잡아도 확실히 갈림. 쪼개져도 부호가 뒤집혀도 걸림.

실화면 확인(8001·5174, `stale:false`) — 측점 2820.0m, 성토사면이 긴 자리:
· 소단 없음 → **≥32.54m** (옹벽·석축 의무 대상)
· 폭 0.5m·3m마다 놓음 → **≥3.00m** (5m 이내 — 의무 해소). 3.00m 은 소단 간격 그대로임
· 옆 측점(2840.0m, 안 놓음) → **≥32.54m 그대로**

**소단 없는 측점은 값이 안 바뀜 — 272측점 전수 대조, 달라진 곳 0.**
구조로도 보장됨(`design.berm` 이 없으면 종전 식을 그대로 탐).

전체 542 passed · 18 skipped. TS 타입 검사 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:59:48 +09:00
eomsangdon 955e7292d4 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-07 16:57:31 +09:00
eomsangdon f6a0eff9b9 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-07 16:57:30 +09:00
eomsangdon 6f6462f60c Merge remote-tracking branch 'origin/sub_laptop_1' into sub_desktop_1 2026-09-07 16:57:12 +09:00
eomsangdon 44fa429f0c Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-07 16:57:09 +09:00
eomsangdonandClaude Opus 5 cc428d7e12 revert(B06): 절토 경사 법정 검사 폐기 — 암질을 횡단도에서 고르지 않음
사용자 확정(2026-09-07): 「연암과 경암 / 발파암과 리핑암 선택은 횡단도에서 선택 안함.
사유는 향후 설계내역에서 설계자가 직접 비율로 지정하기로 함. 암반 지정과 범위 애매모호한
경우가 있어. 실무자는 그렇게 하기로 판단함. 대신 경고부분은 삭제해주고 대신 각도를
사용자가 넣을수 있게 반영.」

**왜 폐기인가** — 판정이 서려면 측점마다 암질(연암·경암)을 못 박아야 하는데, 실무에서
암반 지정·범위가 애매해 그 못 박음 자체가 틀린 전제였음. 암 비율은 **설계내역 단계에서
설계자가 비율로** 넣음. 기준이 없으니 경고도 없음.

되돌린 것 — 1e596846(암질 선택) + 973128f7(별표2 경고). 카드 경고 배지 · 판정 모듈 ·
config 범위표 · context 필드 · 표준 패널 암질 칸 전부 사라짐.

⚠ **남긴 것** — `cut_slope_segments`(구간별 경사)는 지우지 않음. 소단 기하가 그 위에
서 있고 구간 경사를 아는 값이라 뒤에 쓸 자리가 있음. 4e7a4bf7(사면 구간 재료를 그린
경사비로 가름 — 무릎 위 라벨 13구간 오류 수정)도 남김. 검사와 무관하게 옳은 고침임.

시험 4건 — 되살아나지 않게 막는 쪽으로 다시 씀(판정 모듈 없음 · 카드 경고 없음 ·
암질 고르는 자리 없음 · **구간별 경사값은 남아 있음**).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:56:26 +09:00
eomsangdon e3cc364540 Merge remote-tracking branch 'origin/sub_laptop_1' into main_desktop_1 2026-09-07 16:43:15 +09:00
eomsangdon 8bde28e8fe Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-07 16:43:11 +09:00
eomsangdon 36f46a0d94 auto: 2026-09-07 16:43 (EOMSANGDON-HOME) 2026-09-07 16:43:08 +09:00
eomsangdonandClaude Opus 5 1e596846cd feat(B06): 절토 경사 판정 암질을 설계자가 고르게 — 기본 경암
사용자 확정(2026-09-07): 「연암과 경암도 별도의 버튼으로 선택하게 하고 경암을 기본값으로
선택. 리핑을 할지 발파를 할지는 설계자가 선택 필요함.」

**축이 둘이었음 — 섞지 말 것.**
· **암질**(연암·경암) = 별표2 경사 판정의 기준. 설계자가 고름. 기본 **경암**.
· **굴착 공법**(리핑암·발파암, `cut_rock_kind`) = 어떻게 파는가. 수량·단가 몫.
처음에는 공법에서 암질을 유추하려 했는데 **그것이 잘못 세운 문제**였음. 매핑을 걷어내고
암질 선택 하나로 바꿈 — 표준 횡단면 설정 안, 자리는 그대로.

- config 매핑 상수 → `FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT = "hard_rock"`.
- 판정에서 `cut_rock_kind` 를 뗌. 수량 쪽 쓰임은 그대로 둠.
- 실측(용화 63측점·구간 124개) — 기본값 경암에서 **위반 0건**. config 기본 절토비 1:0.4 가
  경암 범위(0.3~0.8) 안임. 토사 63구간도 0건. **기존 설계·수량 안 바뀜.**
  (연암으로 고르면 61건 — 1:0.4 가 연암 하한 0.5 밖이라 그때는 설계 검토가 필요함.)

시험 8건 — 별표2 값 · 작업임도 제외 · **암질은 설계자가 고르고 기본은 경암** ·
**공법으로 암질을 추론하지 않음** · 구간별 판정 · 소단이 있어도 위반이 안 사라짐 ·
경계값 통과 · 카드에 같은 방식으로 붙는지.

7-3 회귀에 소단 한 줄 더함 — 「소단은 값이 아니라 **기하 입력**이라 계산 뒤에 베껴 붙이면
`berm` 값만 남고 계단이 안 그려진다」. 저장분 소단을 **계산 전에** 읽어 넣는 순서를 지킴.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:42:32 +09:00
eomsangdonandClaude Opus 5 db4f17fb1c feat(횡단): 성토면 소단 + 옹벽 의무 판정을 「소단 사이 구간별 최대」로 (계획서 3-9 ⑥)
한 묶음으로 처리했음 — 화면에 5m 판정이 틀린 채 서 있는 시간이 없게.

성토 사면 꼭짓점 짝 신설(`fill_profile_points` · `fillProfilePoints`). 절토와 달리
경사가 하나뿐이라 무릎이 없고, 소단 규칙은 같음. 설계선·지반 교차·꼭짓점 목록이 모두
그 선을 봄.

⚠ **옹벽 의무 판정을 함께 고쳤음** — 「성토사면 길이 5m 이내, 넘으면 옹벽·석축 의무」
(`성토_비탈면.md` §2)를 재는 `fillSlopeLengths` 가 **「성토선은 1:n 직선」을 전제로
수평거리 × 기울기**로 재고 있었음(주석에도 그 전제가 적혀 있었음). 소단이 들어가면 그
전제가 깨져 값이 틀림.

고침 — 소단이 있으면 설계선을 걸어가며 **소단으로 끊긴 한 도막**의 최대 길이를 잼.
· 전체를 한 줄로 재면 소단을 넣어도 5m 를 넘어 **의무가 사라지지 않음**
· 실효 경사로 재면 완만해져 **의무가 사라진 것처럼** 보임
둘 다 틀리므로 「끊긴 한 도막」이 맞는 기준임.

**소단이 없는 측점은 종전 식을 그대로 탐** — `design.berm` 이 없으면 예전 계산 그대로라
값이 한 톨도 안 바뀜(구조로 보장). 화면 전후 대조는 다음 단계에서 냄.

자체검증 — 새 시험 2건(성토면에 계단이 서고 같은 거리에서 덜 내려감 · 소단이 없으면
성토선이 곧은 한 줄). 전체 542 passed · 18 skipped. TS 타입 검사·ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:38:33 +09:00
eomsangdonandClaude Opus 5 a066023da9 merge: sub_laptop_1 되받기 — 저장 흐름 import 충돌 해소
`B06_Section_UI_Page_Persist.ts` 의 import 한 줄만 갈렸음. 양쪽 다 쓰는 이름이라 합침 —
`readByKey`/`writeByKey`(저장소 선택을 한 창구로 모은 것)와 `writeState`(소단 배선)가
같은 파일에서 각각 한 번씩 쓰임. 다른 충돌 없음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:26:57 +09:00
eomsangdonandClaude Opus 5 4e7a4bf787 fix(횡단): 사면 구간 재료를 **그린 경사비**로 가름 — 「토사 경사인데 암」 라벨 없앰
다른 창이 전 측점을 돌려 찾은 것(용화 5601e828 · route 169, 63측점 124구간).
`material: "rock"` 인데 경사비가 1.0(토사)인 구간이 **13개** 나왔음.

까닭 — 재료를 **암반 경계선을 다시 재서** 붙였음. 그런데 무릎을 지난 뒤에도 경계선은
지반을 따라 계속 오르므로, 토사 경사로 그린 구간이 경계 아래로 **되돌아가 있는** 일이
흔함. 그것을 경계로 재면 그린 것과 라벨이 어긋남.

고침 — 재료는 **그 구간을 실제로 그린 경사비**를 따라 붙임(암 경사비에 가까우면 암,
토사 경사비에 가까우면 토사). 두 경사비가 같으면(2단 절토 아님) None.

영향 — 별표2 경사 판정이 갈리던 자리임. 13개가 「암인데 너무 완만」 위반으로 잘못 뜰 수
있었음. 토사 구간 50개는 종전대로 위반 0.

자체검증 — 새 시험 1건: 지반이 가파른 암 지반에서 모든 구간의 재료가 그 경사비와 맞는지
확인(어긋나면 실패). 거울 시험도 통과. 전체 542 passed · 18 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:25:46 +09:00
eomsangdonandClaude Opus 5 973128f798 feat(B06): 절토 비탈 법정 기울기 검사 — 별표2 범위 밖이면 카드에 경고
법정 위반 표시가 없던 유일한 자리였음(평면 곡선반경·성토사면 길이는 이미 있음).
절토 경사비는 표준 횡단면 설정에서 오는 입력값일 뿐이라 범위를 벗어나도 아무 표시가 없었음.

- 기준 — 별표2(지식DB `01_임도/02_상세설계/절토_비탈면.md` §1): 경암 1:0.3~0.8 ·
  연암 1:0.5~1.2 · 토사 1:0.8~1.5. **작업임도는 규정 없음 → 검사 제외.**
- 판정은 **구간별 경사**(`cut_slope_segments`)로 함. 소단이 서면 실효 경사가 완만해져
  **위반이 사라진 것처럼** 보임(폭 1.0·간격 2 이면 1:1 이 1:1.71). 그 함정을 시험으로 못박음.
- 표시는 **기존 방식 그대로** — 성토사면·미폐합 경고와 같은 자리·같은 클래스. 새 방식 안 만듦.
- 지반유형(리핑암·발파암) → 별표2 줄 매핑은 법령 근거가 아니라 **프로그램 설정**이라
  표준 횡단면 설정에서 **사용자가 고르게** 함(2026-09-07 사용자 확정). 기본값 리핑암 → 연암 ·
  발파암 → 경암. 표준단면과 함께 저장돼 이미 사용자 값 계통임.
- 기준값은 서버가 컨텍스트로 내려보냄 — 화면에 상수를 복제하지 않음.

시험 7건 — 별표2 값 · 작업임도 제외 · 매핑이 기본값일 뿐 · 구간별 판정 ·
**소단이 있어도 위반이 안 사라짐** · 경계값 통과 · 카드에 같은 방식으로 붙는지.

⚠ 실화면 — 사용자 선택 칸은 섰음(리핑암 연암 · 발파암 경암). 다만 이 프로젝트 저장분
63측점에 `cut_slope_segments` 가 아직 없어(오늘 새로 생긴 키) 경고가 뜨는 것은 못 봄.
재계산이 한 번 돌면 채워짐 — 지금 경고 0건은 맞는 동작임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:20:42 +09:00
eomsangdonandClaude Opus 5 819aa643dc feat(횡단): 좌측 [소단] 패널 — 구간에 놓고 빼기 + 확정 뒤에도 남게 함 (계획서 3-9)
사용자가 소단을 **직접 놓는** 화면을 만들었음. 프로그램이 「붕괴 우려 지역」을 판정하지
않고, 법정 기준 셋 중 무엇도 자동 적용하지 않음(2026-09-07 사용자 확정).

폼은 C군 구간형 구조물과 **같은 꼴** — 기준 측점 + 전·후 거리로 종단 범위를 잡고
폭·간격·기울기를 함께 받음. 기본값 폭 0.5m · 간격(사면길이) 3.0m · 안쪽 2°.
새 모듈 `B06_Section_UI_Berm_Panel.ts` 로 뺐음 — 좌측 페이지가 이미 700줄을 넘어 있어
거기 더 넣지 않고 두 줄만 꽂음(그 파일 분리는 별건).

세션에는 **구간 목록**으로 둠. 측점별로 펴서 저장하면 「어디부터 어디까지 놓았나」를
되짚을 수 없음. 읽는 자리에서 펴고, 서버에는 측점키 dict 로 실어 보냄.

**확정 뒤에도 계단이 남게** — 소단 제원을 설계 결과에 되싣고(`berm`),
`USER_TOUCHED_KEYS` 에 넣어 재계산이 지우지 않게 함. 서버 재계산은 세션값이 없으면
저장분을 씀(`stored_berm`).

⚠ 소단은 사용자 조작값이면서 **기하 입력**이라 다른 사용자 키와 다름 — 계산 뒤에 키만
베껴 붙이면 설계선·면적은 계단 없이 나오고 `berm` 값만 남아 서로 어긋남. 그래서 단측점
설계 경로는 저장분을 **계산 전에** 읽도록 순서를 바꿨고, 포장 강제·세월교 노면 하강
재계산 경로에도 저장분 소단을 실었음. `extra_spans` 가 그렇게 빠져 있던 자리와 같음.

자체검증 — 새 시험 2건(저장분만으로 같은 설계가 나오는지 · 값이 없거나 손상되면 None).
전체 541 passed · 18 skipped. TS 타입 검사·ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:19:32 +09:00
eomsangdon d48302898f Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 16:09:54 +09:00
eomsangdonandClaude Opus 5 061d550e55 feat(횡단): 소단을 미리보기·재계산에 실어 화면에 계단이 서게 함 (계획서 3-9)
계단이 값에만 있던 것을 화면까지 연결함.

배선 — 세션 열쇠 `berm`(측점키 → 폭·간격·기울기)을 등록표에 두고,
`readBermSession` 으로 읽어 ① 브라우저 재계산(`refreshCrossDesigns`)과
② 서버 미리보기(`cross-design/preview` 의 `berms`) 양쪽에 실음.
암 경계선 오프셋과 같은 길이라 「계획선을 고치면 계단이 사라지는」 일이 없음.

실화면 확인(8001·5174, `/api/health` `stale:false`) — 측점 4120.0m 에 소단을 놓고
계획고를 한 칸 올렸다 내려 전 구간 재계산을 태움.
· 폭 0.5m · 간격 3.0m → 절토 6.84 → 8.10㎡ (토사 2.42→3.24 · 암 4.42→4.86)
· 폭 1.0m · 간격 2.0m → 절토 6.84 → 12.96㎡, 횡단도에 **계단이 눈으로 보임**
· 소단을 안 놓은 옆 측점(4100.0m)은 3.77㎡ 그대로 — 놓은 곳만 달라짐
· 되돌린 뒤 6.84㎡ 로 복귀. [저장]·[확정] 안 눌렀으므로 정본은 그대로.

`cut_slope_segments` 신설 — 절토 사면을 경사 구간별로 쪼갠 목록(파이썬·TS 짝).
법정 경사 검사가 읽을 값임(다른 창 요청). 소단이 서면 사면 전체를 하나로 재는
「실효 경사」가 완만해져 **위반이 사라진 것처럼** 보이므로(폭 1.0·간격 2 이면 설계 1:1 이
실효 1:1.71), 검사는 소단을 뺀 구간 자체를 봐야 함. 실측 — 소단을 놓아도 구간별 경사비는
1.0 그대로 나옴. 평탄부(소단)와 지반 만난 뒤 구간은 싣지 않음.

자체검증 — 거울 시험에 사면 구간 대조를 더해 파이썬·TS 일치 확인.
전체 539 passed · 18 skipped. TS 타입 검사 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 15:28:16 +09:00
eomsangdonandClaude Opus 5 76fa78c528 feat(B05): 구간형 구조물이 놓인 자리를 종단에 늘 띠로 표시
B군(측구·맹암거 등)을 넣어도 **어디에 놓였는지 안 보였음**. 알약은 기준점 한 곳에만 찍히고,
시~종점 띠는 **고른 동안만** 폈기 때문. 측구처럼 길게 이어지는 시설은 「어디부터 어디까지」가
곧 그 시설의 내용이라 늘 보여야 함(2026-09-07 사용자 지시).

- 구간형이면 **늘** 띠를 그림. 고른 것은 두껍고 진하게(4px·0.55), 나머지는 얇고 옅게(3px·0.45).
- **레인 높이는 42px 그대로** — 2-5b 에서 줄여 둔 것을 안 부풀림.
- B05 가 레지스트리를 **한 번만 받아 쓰는 자리에 따라 나눔** — 추가 메뉴는 켜진 종류만
  (종전대로 여섯 군), **알약 레인·군 판정은 전부**. B06 에서 넣은 B군은 레지스트리에서
  꺼져 있어, 켜진 것만 주면 종단에 이름·색 없이 「?」 회색 알약으로 떴을 자리였음.

실측(용화, 구간형 7건) — 띠 **0 → 7개**, 높이 3px, 레인 42px 그대로, 알약 18개 그대로.
B군 6종은 전부 `interval` 이라 확인한 C군과 같은 경로를 탐. 타입 응답 36종에 B군 6종 포함 확인.

평면(지도)은 손대지 않음 — 노선 위에 구간을 얹는 방법이 지금 없어 **별건**으로 둠.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 15:06:53 +09:00
eomsangdonandClaude Opus 5 5a6617f278 fix(횡단): 소단이 암 경계 아래로 되돌아가면 다시 암 경사로 그림 (계획서 3-9)
배분 창 지적으로 확인한 자리 — 미룰 수 없는 쪽이었음.

무엇이 문제였나 — 소단은 평탄한데 암 경계선은 지반을 따라 올라감. 지반이 1:1 이면
폭 0.5m 소단 하나를 지나는 동안 경계는 0.5m 오르고 소단은 2°(0.017m)만 오름. 그래서
설계선이 경계 아래로 **되돌아 들어가는 일이 흔함**. 무릎을 한 번만 꺾는 종전 규칙으로는
그 구간을 **암인데 토사 경사로** 그려 절토가 조용히 커짐 — 경고도 안 뜸.

고침 — `multi_knee` 를 둬서 경계를 오갈 때마다 꺾게 함. **소단이 있을 때만** 켬.
소단이 없을 때 켜면 지금 측점들의 설계가 같이 바뀌기 때문임(실측: 물결 경계 0.0016m).
기본값은 거짓이라 소단을 안 쓰는 프로젝트는 그대로임.

자체검증
- 새 시험 1건 — 경계를 뚫고 올라갔다가 소단마다 뒤처져 되돌아 들어가는 지형에서
  한 번만 꺾는 결과와 갈리고, 되돌아 들어간 구간이 암 경사(가파름)라 같은 거리에서
  더 높이 올라가는 것을 확인.
- 거울 시험에 되돌아 들어가는 경우를 한 줄 더해 파이썬·TS 일치 확인(12건).
- 전체 539 passed · 18 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 14:58:18 +09:00
eomsangdonandClaude Opus 5 e43ad18f97 feat(횡단): 절토 사면에 소단(계단) 기하를 넣음 — 파이썬·TS 짝 (계획서 3-9)
사용자 확정(2026-09-07)에 따라 소단을 **자동 적용하지 않고 사용자가 놓는 것**으로 만듦.
이 커밋은 그 기하 한 벌이고, 화면 폼은 다음 단계임.

새 짝 모듈 `common_util_cross_berm.py` · `.ts` — 절토 사면 꼭짓점을 만듦.
암 경계 무릎과 소단이 한 목록에 함께 들어가고, `breakpoints` 가 그 꼭짓점을 설계선에
실어 도면·면적·유토곡선·3D 가 계단을 그대로 봄.

기본값 폭 0.5m · 간격(사면길이) 3.0m · 안쪽 기울기 2°.
· 폭·간격은 별표2 범위(사면길이 2~3m마다 · 폭 50~100㎝) 안에서 가장 적게 파는 조합임.
  기본값은 되돌리기 쉬운 쪽이어야 함 — 더 넣는 것은 폼에서 한 번이지만 이미 판 것을
  되돌리면 전 측점을 다시 계산해야 함. 실효 경사로도 그러함(경사 1:1 기준)
  폭 0.5·간격 3 → 1:1.24 / 폭 1.0·간격 3 → 1:1.47 / 폭 1.0·간격 2 → 1:1.71.
· 기울기 2°는 사용자 확정값이고 법령·교본 근거가 없어 지식DB 에 적지 않음(사용자 지시).

무릎은 종전대로 **한 번만** 꺾음. 여러 번 꺾게 풀면 소단이 없는 지금 측점 설계도 같이
바뀌므로 별건으로 미룸(실측: 물결 경계에서 0.0016m 차이, 경계를 다시 만나면 구조가 달라짐).

자체검증
- 거울 시험 10건 `tmp/tests/test_cross_berm_mirror.py` — 소단 켠 5경우 포함해 파이썬·TS
  꼭짓점이 1e-9 안에서 일치. 간격이 수평이 아니라 사면길이 기준인 것, 평탄부가 2° 기운 것,
  소단이 없으면 옛 무릎 방식과 완전히 같은 것까지 확인.
- 면적 시험 6건 `tmp/tests/test_cross_berm_area.py` — 절토 55.115 → 78.785㎡(기본값),
  폭·간격이 물량에 단조로 반영, 소단 모서리가 설계선에 실림, 소단을 안 주면 설계선까지 동일.
- ⚠ 어림식 두 번을 시험이 잡아냄. 「늘어난 면적 = 폭 × 그 위 높이」는 틀림(11.797 vs
  7.770㎡) — 설계선이 밀리면 지반과 만나는 점도 함께 밀려 절토가 더 길어짐. 「폭만큼 밀림」도
  틀림 — 참값은 「그 아래 소단 개수 × 폭」임. 시험은 그 참값으로 잼.
- 기존 거울 시험(`test_b06_cross_design_mirror.py`) 10건 그대로 통과 — 리팩터가 값을
  안 바꿨다는 증거임. 전체 537 passed · 18 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 14:54:18 +09:00
eomsangdonandClaude Opus 5 8a0a8f18e3 feat(화면): 화면 배치를 계정에 저장 — PC 를 바꿔도 따라오게
사용자 승인(2026-09-07). 앞서 취향을 localStorage 로 옮겨 **탭·재시작** 문제는 풀었고,
이 변경은 그 위에 **다른 PC 에서도 같은 배치**를 얹음. 사용자가 노트북·데스크톱 두 대를 오감.

- `db_management/018_user_ui_prefs.sql` — 사용자당 한 줄, `prefs` JSON 한 칸.
  칸을 나누면 취향이 늘 때마다 마이그레이션이 또 필요해 한 칸에 담음.
- `GET/PUT /api/dashboard/me/ui-prefs` — 배치 값만. **설계값은 안 담음**(문자열만 받음).
- 로그인이 확인된 첫 순간에 한 번 받아 로컬 위에 얹고, 취향이 바뀌면 1.5초 모아 올림.
  서버가 없거나 못 읽으면 **로컬 값으로 그대로 돔**(계획서 원문).

⚠ 같은 함정을 또 밟을 뻔했음 — 키만 아는 자리가 저장소를 직접 고르면 **올려보내기도 안 걸림**.
그래서 `storageOf` 를 없애고 **읽기·쓰기 창구 하나**(`readByKey`/`writeByKey`)로 모음.
저장소 선택과 서버 올려보내기가 그 한 곳에만 있음. 그물도 그 이름으로 갱신.

**DB 는 아직 적용하지 않았음** — 표가 없으면 API 가 실패하고 화면은 로컬 값으로 도는 것이
정상 동작임. 적용 시점은 다른 창들과 맞춘 뒤.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 14:50:31 +09:00
eomsangdonandClaude Opus 5 159e2a5226 refactor(B06): 등록표 키를 쓰는 자리가 저장소를 직접 고르지 않게
오늘 같은 계통이 두 번 났음 — 키는 등록표에서 받는데 **저장소는 제 맘대로** 고르는 자리.
· `rockb` — 키를 날문자열로 만들어 읽는 쪽이 늘 빈 값(`f8fafd23`)
· 화면 취향 — 키는 맞았는데 저장소를 직접 골라 **절반만** 옮겨짐(`9db84d9d`)

남은 두 자리(암 경계선 저장·표시 반폭)도 `storageOf` 를 거치게 함. 지금은 둘 다 초안이라
세션이 맞지만, 통이 바뀌면 조용히 갈릴 자리였음.

그물도 넓힘(`test_session_keys_registered.py`) — 등록표 키를 쓰는 파일이 저장소를 이름으로
직접 고르면 깨짐. 고르는 곳은 `b_page_state` 하나. 앱 전역 값(`CURRENT_PROJECT_ID_KEY`)은
등록표 밖이라 제외하고 이유를 적어 둠. **훑어 보니 남은 위반 0곳.**

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 14:41:38 +09:00
eomsangdon a086b3c124 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 14:39:15 +09:00
eomsangdonandClaude Opus 5 9db84d9d20 fix(화면): 패널 높이·접힘이 탭을 새로 열 때마다 초기화되던 것
화면 취향까지 sessionStorage 에 있어 **탭을 새로 열거나 브라우저를 껐다 켜면** 패널 높이·
접힘·유토곡선 열림이 매번 초기화됐음. 계획서에는 「PC 를 바꾸면」으로 적혀 있었으나 실제
불편은 훨씬 잦았음(사용자 승인 뒤 조사에서 드러남).

- 저장소를 통(bucket)으로 가름 — **`pref` 만 localStorage**, 초안·결과는 세션 그대로.
  설계 데이터가 아니므로 CLAUDE.md 5장 「캐시=세션」에 걸리지 않음.
- 키만 들고 저장소를 직접 만지던 화면 공용 부품 셋(`ui_template_resizer`·`_overlay`·
  `_overlay_drag`)이 같은 판단(`storageOf`)을 쓰게 함 — 안 그러면 높이 값만 세션에 남아
  절반만 옮겨졌음(실측: 접힘 4개는 옮겨지고 높이 3개는 안 옮겨짐).
- 첫 로드 때 세션에 남은 옛 취향을 한 번 쓸어 옮김 — 이 변경으로 쓰던 크기가 초기화되지 않게.

실측(공용 브라우저) — 옮긴 뒤 취향 8개 전부 localStorage, 세션 0개, 높이 보존
(종단 438px · 표 204px). **새 탭에서도 8개 그대로**(같은 값). 검증 탭은 닫음.

시험 `tmp/tests/test_prefs_persist_across_tabs.py` 4건 — 취향만 갈리는지 · 초안·결과는
세션인지 · 공용 부품이 같은 판단을 쓰는지 · 옛 값을 한 번 옮기는지.

이 조각은 DB 없이 되는 몫임. 계정 저장(`018_user_ui_prefs`)은 다음 단계.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 13:51:58 +09:00
eomsangdonandClaude Opus 5 7769db3639 feat(구조물): 측구는 「횡단 설계에서 관리」로 표시하고, B군 연장(m) 집계를 만듦 (계획서 3-6)
사용자 결정 두 가지를 반영함(2026-09-07).

① 측구(옆도랑) — 목록에 두되 표시만.
레지스트리에 `design_owner` 를 새로 두고 측구에 「횡단 설계」를 적음. 구조물 배치 폼에서
그 종류를 고르면 「횡단 설계에서 관리 — 여기서 넣어도 제원·수량은 그쪽 값을 씁니다.」가
뜸. `managed_by` 와 달리 저장은 그대로 되므로 배치는 계속 가능함.

까닭 — 횡단 설계가 측구 켬/끔·형식·터파기 단면적(`ditch_area_m2`)을 파이썬·TS 짝으로
이미 셈함. 구조물로 또 세면 같은 것을 두 번 계상함.

목록 항목에 안 붙이고 폼에 붙인 것은 「한 항목 = [측점][이름]만」이 2026-08-18 사용자
지시이기 때문임.

② B군 수량 1단계 — 구조물 정본에서 시설별 연장(m)을 냄.
`common_util_structure_lengths.py` 하나. B군 수량 단위가 전부 m 이라 단면 기하가 필요
없음(맹암거 12-10 · L형 측구 12-9-1 · 산마루측구 12-9-2). 수량서 양식에 안 묶이므로
B08 을 실무 xlsx 양식으로 다시 짜도 그대로 씀.

빼는 것 셋 — 관 정본 소관(`managed_by`), 횡단 소관(`design_owner`), 그리고 소단측구
(놓일 소단이 아직 없음 — 계획서 3-9 뒤에 채움).

겹친 구간은 합쳐서 셈함. 같은 시설을 겹치게 두 번 넣으면 단순 합이 그 구간을 두 번
세기 때문임. 원래 합(`raw_length_m`)도 함께 내보내 겹침이 숨지 않게 함.

자체검증
- 새 시험 7건 `tmp/tests/test_structure_lengths.py` — 종류별 합산 / 겹침 제거(80m 인데
  단순 합 110m) / 측구 제외 / 소단측구 제외 / 배관 제외 / 구간 없는 항목은 스키마가 막음
  / 빈 프로젝트.
- 화면 확인(8001·5174, `/api/health` `stale:false`) — 구조물군 B → 종류 측구 선택 시
  안내가 254×44px 로 뜨고 문구가 맞음. 리셋으로 폼 되돌림.
- 전체 521 passed · 18 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 13:39:42 +09:00
eomsangdon ed982c9227 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 11:59:57 +09:00
eomsangdonandClaude Opus 5 df02793caf feat(dev): 지금 도는 서버가 옛 코드인지 헬스체크가 알림
자동 리로드가 조용히 죽어 **옛 코드로 도는 서버**에 오늘 두 창이 세 번 속았음
(속도 488초 오측 · 응답에 새 필드가 없는데 화면은 정상으로 보임 · 표준단면 응답이 옛 스키마).
매번 틀린 결론을 한 번씩 냈다가 되물렀음. 규칙("재기 전에 재시작")으로는 세 번 다 안 지켜져
**한 번 보면 아는 표시**로 바꿈.

- `/api/health` 가 개발 모드에서 `started_at` · `code_mtime`(기동 때 본 소스 최신 수정) ·
  `source_mtime`(지금 소스 최신 수정) · `stale` 을 함께 냄. `stale: true` 면 재시작 전
  어떤 측정도 믿으면 안 됨.
- 기동 로그에 감시 폴더 수와 소스 최신 수정 시각을 한 줄 남김.
- 실측 — 감시 폴더의 `.py` 를 고치니 리로드는 **안 뜨고** `stale` 이 즉시 true 로 바뀜.

원인 추적은 계속함(PLAN 7-2) — 한글 경로·정션은 **아님**(`watchfiles` 를 그 폴더에
직접 걸어 보니 변경을 정상 감지). uvicorn 이 감시 폴더 12개를 선언하고도 리로드를
안 내는 자리라 더 파야 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:57:33 +09:00
eomsangdon d622a921e8 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 11:57:03 +09:00
eomsangdonandClaude Opus 5 e2d329d35f fix(배수유역): 지문이 달라도 같은 노선이면 관 지점을 그대로 씀 (계획서 0-7 곁가지 ③)
같은 노선인데 지문이 늘 어긋나 관 지점이 매번 투영 이월을 탔음.

까닭 — 같은 노선을 두 파일이 다른 자릿수로 담고 있음.
`planned_route.csv` 는 소수 4자리(`208403.2001`), `route_main.geojson` 은 소수 3자리
(`208403.2`)로 csv 를 mm 반올림한 사본임. 좌표 차는 최대 0.5mm 뿐인데, 지문이
`f"{x:.2f}"` 로 0.01m 자리에서 끊는 탓에 그 0.5mm 가 `.xx5` 경계를 넘는 정점마다
글자가 바뀜 — 169개 중 16개가 그랬음.

경계에서 자르는 방식은 저장 자릿수가 또 바뀌면 다시 흔들리므로, 글자 일치 대신
**허용오차**로 가름. 저장된 관을 지금 노선에 투영해 **재 보기만** 하고
(`max_projection_shift`, 값은 안 고침), 최대 어긋남이 0.05m 이하면 같은 노선으로 보고
저장분을 그대로 돌려줌.

허용오차 0.05m 근거 — 실측 어긋남이 최대 0.0053m 이라 다섯 배 이상 여유이고,
사람이 노선을 실제로 고치면 관은 m 단위로 밀리므로 「같다」로 볼 위험이 없음.

투영 이월 가지는 그대로 둠 — 대신 그 가지가 실제로 돌면 WARNING 을 찍게 함.
한동안 0 인 것을 확인한 뒤에야 지울 수 있음(먼저 지우면 관이 통째로 사라짐).

자체검증
- 새 시험 3건 `tmp/tests/test_pipe_route_tolerance.py` — mm 반올림 사본은 같은 노선으로
  판정되고 누가거리가 한 값도 안 움직임 / 중간을 3m 민 노선은 안 걸림 / 허용오차 범위.
- 기존 `test_pipe_point_projection.py` 2건 그대로 통과(±0.7m 잔물결 노선은 여전히 투영).
- 저장된 실제 3개 프로젝트 전후 대조 — 셋 다 「다름 → 투영」이 「같음 → 저장분 그대로」로
  바뀜. 화면 값 변화는 최대 0.0053m · 0.0009m · 0.0005m.
- 전체 514 passed · 18 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:54:23 +09:00
eomsangdonandClaude Opus 5 72e50b3edd fix(B06): 저장된 표준 횡단면이 새 탭에서 화면에 안 서던 것
표준단면 편집값은 sessionStorage 에만 살아 **탭을 새로 열면** 사라졌음. 그때 브라우저는
config 기본값으로, 서버는 저장분으로 계산해 **같은 측점이 갈렸음**. 표준단면은 모든 측점의
횡단 모양을 정하므로 면적·유토곡선·수량까지 그대로 흐름.

- `sections/context` 가 저장분을 **한 칸 더** 실어 보냄(`stored_standard_cross_section`).
  기존 `standard_cross_section`(config 기본값)은 그대로 둠 — 옛 화면 안 깨짐, 마이그레이션 없음.
- 브라우저는 두 화면이 함께 지나는 한 자리(`fetchSectionContext`)에서 **세션이 비었을 때만**
  그 값으로 세움. 그 탭에서 고친 값이 있으면 안 건드림(초안 우선).

실측(용화 5601e828 · route 169):
- 고치기 전 — 저장분 암반 횡단경사 **5%** · 측구 상단폭 **0.9m** 인데
  화면이 받는 값은 **3%** · **0.69m**(config 기본값)였음. 응답에 저장분 칸 자체가 없었음.
- 고친 뒤 — 응답에 0.9·5 가 실리고, **빈 새 탭**에서 B06 을 열면 세션이
  `rock.ditch.top_width_m=0.9` · `rock.cross_slope_pct.max=5` 로 섬(검증 탭은 닫음).

시험 `test_b06_stored_standard_reaches_browser.py` 3건 — 칸이 따로 있고 기본은 None ·
라우터가 저장분을 실음 · 브라우저가 세션이 빈 경우에만 세움(두 갈래 모두).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:47:27 +09:00
eomsangdonandClaude Opus 5 a622bcb964 fix(세션): 노선 변경 때 옛 노선 초안·결과가 안 지워지던 것 (계획서 0-7 곁가지 ②)
노선을 다시 계산하면 세션의 옛 노선치가 그대로 쌓였음. 용화(5601e828)에 145·161·169
세 노선치가 남아 있는 것을 실측함.

원인은 「안 지웠다」가 아니라 **못 지웠다** — 노선 변경 자리는 새 번호를 아직 모르니
`clearDrafts(projectId)` 로 부르는데, `stateKey` 가 노선 번호 없이는 `null` 을 내어
route 범위 키가 한 개도 안 나갔음. 부르는 쪽 주석은 「남기지 않는다」였고 실제로는 다 남았음.

`clearState` 한 곳에서, 노선 범위인데 노선 번호가 없으면
`aislo:<통>:<이름>:<프로젝트>:` 로 시작하는 키를 모두 쓸어 내게 함.
`clearDrafts` 와 `clearResults` 가 함께 나음. 노선 번호를 주면 종전대로 그 한 벌만 나감.

안전 확인 — 옛 노선치를 읽는 자리 없음(`readState` 호출 8곳 전부 전역·프로젝트 범위).
노선 복원도 새 route id 를 발급하므로 옛 키는 다시 안 불림.

자체검증 — `tmp/tests/test_page_state_route_sweep.py` 3건 신설.
옛 코드로 같은 시험을 돌리면 세 벌이 그대로 남아 깨지는 것까지 확인(헛도는 시험 아님).
전체 511 passed · 18 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:40:03 +09:00
eomsangdonandClaude Opus 5 647ec367d0 docs(A00): 표준단면 초안은 저장 뒤에도 유지임을 등록표에 명시
`std-cross` 가 [저장] 뒤에도 안 비는 것이 실수인지 의도인지 갈렸음 — **의도로 확정.**
다른 초안과 달리 이 값은 브라우저가 가진 **유일한** 사용자 표준단면임:
`sections/context` 가 주는 `standard_cross_section` 은 저장분이 아니라 config 기본값이고
(`B06_Section_Router.py:132`), 저장분은 서버 안에서만 쓰임
(`stored_standard_cross_section`). 비우면 브라우저 횡단 계산이 그 순간 config 기본값으로
되돌아감. [초기화]에서만 버림.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:37:49 +09:00
eomsangdonandClaude Opus 5 f8fafd2300 fix(B06): 암 경계선 오프셋을 읽는 쪽이 옛 키를 보던 것
쓰는 쪽은 `stateKey("rockb", …)` = `aislo:draft:rockb:{p}:{r}` 에 쓰는데, 읽는 쪽
`readRockBoundarySession` 이 옛 키 `b06:rockb:{p}:{r}` 를 날문자열로 읽고 있었음.
`writeStateRaw` 는 옛 키를 안 쓰므로 **읽는 쪽이 늘 빈 값**을 받았음.

그래서 계획선을 고쳐 횡단을 다시 계산할 때(`B06_Section_Cross_Refresh`) 사용자가 옮긴
암 경계선이 안 실려 나가 **토사/암 나눔이 기본값으로 되돌아갔음** — 수량이 갈리는 자리.
[저장] payload 는 메모리 Map 을 써서 저장 자체는 무사했음.

- 읽는 쪽을 등록표(`readState("rockb", …)`)로 바꿈. 옛 키 만드는 함수는 지움.
- 실화면 실측(용화 5601e828 · route 169) — 경계선 -0.5 → -0.8m 로 옮기니
  새 키에 `{"0.00":-0.8}` 가 쌓이고 **옛 키는 아예 없었음**(고치기 전 근거).
  고친 뒤 새로고침해도 값·표기 그대로.

시험 `tmp/tests/test_session_keys_registered.py` 3건 — 등록표 밖 날문자열 세션 키를
쓰면 깨지는 그물 + 이 자리 못박기. **훑어 보니 다른 날문자열 키는 0개**로,
`rockb` 가 유일한 구멍이었음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:31:07 +09:00
eomsangdon 05c4a2ef65 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 11:15:25 +09:00
eomsangdonandClaude Opus 5 b8f0f7ad9d fix(B06): 사용자가 켠 2단 절토가 재계산에서 사라지던 것
엔진 둘이 결과 딕셔너리에 **엔진이 실제 적용했는지**를 담아 저장했고, 그 값이 다음
재계산에 인자로 되먹여졌음. 그래서 토사 측점처럼 2단을 못 쓰는 자리에서 사용자가 켜 두면
저장값이 False 로 바뀌고, 그 False 가 다시 인자가 되어 **켬이 영구히 사라졌음.**

- `two_stage_slope` 를 **받은 요청값 그대로** 내보내게 함(파이썬·TS 짝 두 줄).
- 「실제로 적용됐나」를 읽는 곳은 코드 전체에 없음(전수 확인) — 읽는 쪽 여섯은 모두
  사용자 설정으로 씀. 2단 사면이 그려지는 근거는 이 값이 아니라 설계선 기하임.
- 저장 형식 안 바뀜. 옛 저장분도 그대로 읽힘.

시험 `test_b06_two_stage_slope_kept.py` 4건 — 토사에서 켬 유지 · 되먹임 왕복 ·
끔도 유지 · 「2단은 설계선이 그린다」(암은 기하가 갈리고 토사는 같음) 확인.
거울 시험은 기존 `test_b06_cross_design_mirror.py` 가 그대로 잡음.

같은 모양인 `ditch_enabled` 은 손대지 않음 — 그쪽은 저장값을 「측구 없음」 판정으로
읽는 곳이 4군데(횡단 맞춤·횡단 그리기·B07 도면·3D 카브)라 처방이 다름.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 10:57:09 +09:00
eomsangdonandClaude Opus 5 8f4c486a00 fix(개발환경): vite 프록시 대상을 127.0.0.1 로 박음 — /api 가 IPv6 로 헛짚던 것
계획서 7-1. 화면의 **모든 `/api` 요청이 「Failed to fetch」** 가 되는데 서버는 멀쩡한
증상이 하루 종일 있었음. 원인은 프록시 대상이 `http://localhost:<포트>` 인 것 —
Node 18+ 는 `localhost` 를 **IPv6(::1) 먼저** 물고, 백엔드는 `SERVER_HOST=0.0.0.0`
(IPv4 전용)으로 떠서 서로 못 만남.

실측 — `127.0.0.1:8001` 200 · `[::1]:8001` 연결 실패.
고친 뒤(vite 재기동 포함) 화면에서 `/api/health` **200**, 로그인 POST **200**.

오늘 겪은 「저장 Failed to fetch」 두 번과 「화면 백지」 세 번이 이것으로 설명됨 —
부팅 때 API 가 죽으면 앱이 아무것도 안 그리고 콘솔 오류도 안 남았음.

공용 파일이라 배분 창(claude-b1) 승인을 받고 고침. 5173·5174 양쪽에 같이 적용됨.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 10:50:36 +09:00
eomsangdonandClaude Opus 5 744059aea1 refactor(B06): 사용자 값 목록을 한 벌로 — 손나열 두 곳 제거
새 사용자 값을 더할 때 **네 곳을 다 고쳐야** 했고, 하나만 빠지면 그 값이 저장에서
조용히 사라졌음(`extra_spans` 실사고 `b6941bd2`). 나열을 걷어 목록 한 벌로 모음.

- 확정·저장 payload 조립(`_Router_Confirm.py`) — `if patch_item.X is not None:` 11개를
  스키마 통째 덤프 + 최상위 None 걷기로 바꿈. 새 필드가 저절로 실림.
  중첩 None 은 남김(기슭막이 4축 `d: null` 은 「자동」이라는 뜻).
- 브라우저 payload(`_Section_Store.ts`) — `put("X", design.X)` 10줄을 목록 순회로.
- `_Cross_Refresh.ts` 에 `USER_TOUCHED_KEYS` 를 내보내고 `PRESERVED_KEYS` 는
  거기에 상태 둘을 더해 만들게 함. 서버 목록과 짝임을 머리에 적음.

전수 조사 결과 `design` 키는 세 갈래였음 — ① 재계산 인자로 되먹여 사는 것 8개
② 목록으로 베껴야 사는 것 12개 ③ 순수 계산값 24개. 위험한 칸은 ② 하나뿐임.

시험 3건 추가:
- `test_b06_design_key_split.py` — 사용자 값이 ①·② 어디에도 안 걸리면 깨짐(불변식).
  지금 코드는 통과 = 알려지지 않은 구멍 없음.
- `test_b06_recompute_keeps_user_values.py` — 포장 재계산 뒤 ② 값 보존·① 인자 전달.
- `test_b06_user_touched_keys.py` — 손나열이 되살아나면 깨지는 검사 둘 추가.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 10:49:29 +09:00
eomsangdonandClaude Opus 5 09442ae165 fix(B06): 단이 못 섰다는 안내를 조정창 밖 경로에서도 띄움
3-7 (2026-09-07). 안내는 이미 있었으나 `activeRevet` 이 그 벽일 때만 떠서, 좌측 「구조물
배치」 폼처럼 **조정창을 안 거친 경로**로 단 수를 바꾸면 개수만 조용히 잘렸음. 사용자는
[+]를 눌러도 아무 일이 없는 것으로 보임.

- 조건을 없애고 **자리(측점·측·세워진 단 수)마다 한 번만** 내도록 바꿈. 다시 그릴 때마다
  뜨면 시끄럽고, 결과가 달라지면(지형·조작) 키가 달라져 다시 알림.
- 요청대로 다 선 자리는 기록을 지워, 나중에 또 모자라면 다시 알림.
- 문구는 기존 `B06_Cross_Extra_Limit` 그대로 — 새 문구를 만들지 않음(중복 토스트 방지).

앞서 `Station_Controls.syncCount` 에 같은 안내를 붙였다가 **기존 것과 겹쳐 두 줄로 뜨는 것**을
실화면에서 보고 되돌렸음. 안내는 이 한 자리에만 둠.

tsc 통과 · 시험 508 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 10:04:22 +09:00
eomsangdonandClaude Opus 5 023c0d545a fix(B06): 독립 기슭막이에도 좌우 이동 안내 + 하한 알림 조건 좁힘
- 독립 기슭막이(D군) 툴팁은 **따로 만들어져** 있어 「지형에 막힘」조차 안 떴음.
  배관 벽과 같은 두 줄(지형 막힘 · 각도 하한)을 같은 문구로 붙임.
- 기준벽 경로(`computeRevetmentLayout`)에도 `shiftFloorM` 을 남김 —
  하한이 `1.2 × 상하 내림 − 자동 자리`라 d 2.6m 자리에서 1.0m 요청이 삼켜졌음.
- 하한 알림은 **사용자가 실제로 값을 넣었을 때만** 뜨게 좁힘. 요청이 0이면
  하한은 그냥 기본 자리라 모든 벽에 뜨면 소리만 됨.
- 문구를 두 경로에 맞게 고침 — 「상하 내림의 1.2배」가 아니라
  「성토선 각도(1:1.2)를 지키려면 그만큼은 나가 있어야 함」.
- 시험 3건(`test_b06_extra_shift_floor.py`) — 하한 적용·알림·요청 없을 때 침묵.

미확인 — 실화면에서 알림이 뜨는 것까지는 못 봄. 지금 프로젝트(용화)의 카드가
전부 링크 카드라 좌우 이동 조작 자체가 먹지 않아 조건을 세우지 못했음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 10:02:03 +09:00
eomsangdonandClaude Opus 5 ec153bc2e8 fix(B06): 다단 기슭막이 좌우 이동이 말없이 안 먹던 까닭 알림
「좌우 1.0m 를 넣어도 안 움직인다」의 원인은 지형이 아니라 **선반 하한**이었음 —
좌우 이동은 선반 길이가 음수가 되지 않게 `1.2 × 상하 내림`까지 따라 나가므로,
저장값이 d 2.6m 이면 하한이 3.12m 라 1.0m 요청은 아무 변화도 못 냄. 그런데도
`shiftBlockedM`(지형에 막힌 양)은 0 이라 까닭을 알 길이 없었음.

- `shiftFloorM` 추가 — 요청이 하한에 눌려 통째로 무시된 경우 그 하한을 남김.
- 벽 툴팁에 한 줄 — 하한 값과 「그 아래로 넣으면 안 움직임」을 적음.
  지형 탓(`shiftBlockedM`)과 자리를 나눠 두 까닭이 안 섞이게 함.
- 시험 `tmp/tests/test_b06_extra_shift_floor.py` — 화면 코드를 그대로 컴파일해
  Node 로 돌림. 좌우 0.5m 요청이 d 2.0m 아래에서 2.4m 로 밀리고 그 값이 알려지는 것 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:49:14 +09:00
eomsangdon af207bfbb4 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 09:37:46 +09:00
eomsangdonandClaude Opus 5 692dc1dbaa refactor(B05): 노선 편집 모달 곡선 셈 분리 — 700줄 제한 준수
- `B05_Profile_UI_RouteEdit_Curve.ts` 신설(68줄) — 손잡이 끌기 역셈
  (`dragHandleTo`)·직선 교점(`intersect`)·내각(`innerAngleDeg`) 순수 기하만.
- 모달은 740 → 692줄. 화면 배선·동작 불변, 셈 식 그대로 옮김.
- 서버 정셈과 짝임을 새 파일 머리에 명시. 거울 시험은 새 자리를 보게 갱신.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:36:32 +09:00
eomsangdonandClaude Opus 5 55895c3eaf build: watchfiles 추가 — 자동 리로드를 이벤트 방식으로
사용자 승인(2026-09-07). 없으면 우버콘이 StatReload 로 떨어져 0.25초마다
감시 폴더를 통째로 훑고, 그러면 요청이 없어도 CPU 를 먹음.

실측 (놀고 있는 서버) — 감시 폴더를 좁히기 전 **70%** → 좁힌 뒤 **18%** →
이 패키지 설치 뒤 **0%**. 기동 로그가
`Started reloader process [...] using WatchFiles` 로 바뀜.
보조 창(8001)도 같은 값 확인 — 리로더 0% · 서버 0%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:11:43 +09:00
eomsangdonandClaude Opus 5 136965c472 fix(B06): 알약 레인이 종단 패널 높이를 부풀리던 것
④(2026-09-07)로 붙인 레인이 `chartWrap` 안에 서는데, 그래프 높이를 정할 때 잰 값에서
레인 몫을 빼지 않아 되먹임이 생겼음 — 잰 값이 커지면 그래프를 키우고, 그러면 다음 회차에
더 커짐. 실측 패널이 **16,664px** 까지 부풀어 카드·알약을 누를 수 없었음.

레인 높이(`STRUCTURE_LANE_HEIGHT_PX`)를 빼서 한 번에 수렴하게 함.
자체검증 — 패널 242px · 그래프 228px · 레인 42px · 알약 10개. tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:07:09 +09:00
eomsangdonandClaude Opus 5 a0bd9c8775 fix(B05): 한 번 편집하면 맞춰 둔 반지름이 하한으로 눌리던 것
실화면에서 잡음 — 곡선 하나만 지워도 나머지 반지름이 **R 12~199m → 전부 12m**
로 되돌아갔음.

원인은 편집 모델과 저장 모델이 다른 것이었음. 서버는 처음 만들 때 이어진
꺾임을 한 곡선으로 묶는데, 그 곡선의 교각점은 앞뒤 직선을 늘려 만나는 자리라
**원본 꺾임점 중 어느 것도 아님**. 편집은 「꺾임점 하나 = 곡선 하나」로
표현되므로, 묶인 채로 두면 한 번만 손대도 묶음이 낱개로 흩어지고 각 자리가
하한으로 떨어졌음.

모달이 자료를 읽을 때 **묶인 구간을 그 교각점 하나로 갈아 끼우게** 함 —
안쪽 꺾임점은 그 곡선이 대신하므로 뺌. 앞뒤 직선과 반지름이 그대로라
그려지는 선은 똑같고, 이제 손대도 안 흩어짐. 서버가 고른 반지름도 함께
들고 가 [확인] 때 되돌려 보냄.

시험 tmp/tests/test_route_polyline_apex_roundtrip.py 2건 — 갈아 끼워도 곡선
수·반지름이 같고 선이 **1mm 안**에서 일치, 그리고 안 갈아 끼우면 실제로
하한으로 눌리는 것(왜 필요한지)도 못박음.

실화면 확인(용화) — 되돌린 뒤 곡선 26곳 R 최대 198.9m → 곡선 하나 지우고
[확인] → 곡선 25곳, **R 42.5·51·198.9m 그대로**.

tsc --noEmit 통과 · pytest 432 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:46:09 +09:00
eomsangdonandClaude Opus 5 20f343902d feat(B05): 곡선 시작·끝점을 끌면 직선 각도와 R 이 함께 바뀜
사용자 확정(2026-09-07) — 「직선과 곡선 교차점 이동 시에는 직선의 각도와
반지름 값 변경이 되」. 되물어 확정: 끄는 점은 **곡선 시작·끝점**.

셈은 서버의 반대 방향임 — 서버는 교각점·R 에서 접선점을 내고, 화면은 끈
접선점에서 교각점·R 을 구함. 끈 쪽 직선만 돌리고 반대쪽은 옛 교각점을 지나는
그대로 두어, 두 직선이 만나는 자리가 새 교각점이 되고
R = |새 교각점 − 끈 자리| / tan(교각/2) 로 나옴.

**두 벌이 아니라 짝임** — 왕복이 제자리인지를
tmp/tests/test_route_polyline_handle_drag.py 3건이 지킴. 화면이 낸 (교각점, R)
을 서버 셈에 넣으면 끈 자리가 **1µm 안**으로 되돌아옴. 접선점을 직선 위에서만
밀면 교각점이 안 움직이고 R 만 커지는 것도 확인.

손잡이를 **속 빈 네모(5px)** 로 키움 — 3.5px 채운 네모는 선·노드와 색이 같아
눈에도 안 띄고 집기도 어려웠음(실화면). 곡선을 지운 자리에는 손잡이도 없음.

실화면 확인(용화) — 손잡이를 끌자 「14번째 꺾임점 · R 16.6 · 값 지정」으로
바뀜. 서버가 곡선 성분 22개(예: R 44.3m, 노드 1~3을 한 곡선으로)를 내려줌.

⚠ 그 확인 도중 **서버가 또 옛 코드로 돌고 있었음**(응답에 `curves` 키 자체가
없었음). 오늘 두 번째임 — 화면 검증 전에 응답에 새 필드가 있는지부터 볼 것.

tsc --noEmit 통과 · pytest 430 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:34:04 +09:00
eomsangdon 39fbdb9539 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 08:21:55 +09:00
eomsangdonandClaude Opus 5 679726124b feat(B06): 종단 구조물 표시를 B05 와 통일 — 같은 알약 레인
PLAN 2-5 (2026-09-07 사용자 지시 4 「구조물 표시 통일」). B06 종단에는 구조물 마크가
아예 없었고 배수관·세월교만 다른 계통으로 그려졌음.

- B05 가 쓰는 **같은 부품**(`buildStructureLane`)을 B06 종단 그래프 아래에 붙임.
  이름·중복 번호·툴팁·약호가 전부 B05 규칙 그대로 따라옴.
- 종단 차트가 `toX`(누가거리 → px)를 내보내 레인이 그래프와 같은 자리를 씀.
- 좌측 「구조물 배치」가 목록을 받을 때마다 `setStructureMarks` 로 넘김(구조물 정본 +
  관 정본). 관을 가상 구조물로 바꾸는 `pipesToStructureMarks` 는 B05 안에 있던 것을
  공용 자리(`B05_Profile_Api_Structures`)로 올려 **한 벌만** 둠.
- 알약을 누르면 그 자리에 가장 가까운 측점 카드를 고름(좌측 목록 클릭과 같은 규칙).
  끌기는 B05 몫이라 여기서는 자리만 보임(배수유역 재분할이 걸림).
- 검증 훅 `window.__b06Marks` 추가(`__corridorBuild` 등과 같은 용도).

자체검증 — 실화면 B06 종단에 레인 1줄·알약 10개(「배수관 1~8 · 세월교 1~2」)가 B05 와
같은 표기로 뜸. 훅 값 `{structures:10, types:36}`. tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:20:59 +09:00
eomsangdonandClaude Opus 5 43d55f498c fix(B05): 곡선 편집줄이 캔버스 밑에 깔리던 것
실화면에서 [곡선 지우기] 클릭이 캔버스에 가로채였음 —
「<canvas …> subtree intercepts pointer events」.

원인은 세로 배치임. 상자가 세로 flex 이고 캔버스 자리가 `flex: 1 1 auto` 라
공간을 다 먹는데, 새로 넣은 편집줄에 `flex: none` 이 없어 눌려 없어지고
캔버스가 그 위를 덮었음. 바닥 단추줄과 같이 `flex: none` 을 주고
`z-index: 1` 로 올림.

실화면 재확인(용화, 20번째 꺾임점) — 네 조작 모두 동작:
  고름   → 「자동 · 내각 155° · 법정 하한 12m」
  곡선끔 → 「곡선 없음 — 직선이 그대로 꺾입니다」 · 상태줄 「곡선 지움 1곳」
  곡선켬 → 「자동」으로 복귀
  R 40   → 「값 지정」 · 상태줄 「R 지정 1곳」
  자동   → 값 비고 「자동」으로 복귀

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:19:06 +09:00
eomsangdonandClaude Opus 5 56a6d18611 feat(B05): 노선 편집 모달에 곡선 제어 — R 칸·곡선 지우기/넣기·시작끝점 표시
사용자 지시(2026-09-07) — 「r과 직선 삭제나 추가가 있어야 하지 않을까?」
서버 쪽(f59932de)에 이어 화면을 붙임.

  - **곡선 시작·끝점을 네모로 표시** — 어디부터 어디까지가 한 곡선인지 눈으로 봄.
  - 꺾임점을 누르면 **고른 자리**가 되고 아래 편집줄이 그 곡선을 만짐.
  - **R 입력칸** — 값을 넣으면 그 반지름으로 못박고, [반지름 자동]으로 되돌리면
    서버가 예정노선에 맞춰 다시 고름.
  - **[곡선 지우기] / [곡선 넣기]** — 지운 자리는 가운데가 빈 동그라미로 보이고
    직선이 그대로 꺾임.
  - 직선 삭제·추가는 원래 있던 조작 그대로(오른쪽 클릭 삭제 · 두 번 클릭 삽입).
    다만 그때 **편집값 목록도 같은 자리에서 밀어 준다** — 안 그러면 뒤 노드의
    R·켬끔이 한 칸씩 어긋남.
  - [확인]이 정점마다 `curve`·`radius_m` 을 함께 보냄.

상태줄에 「곡선 N곳(하한 R m) · 곡선 지움 N곳 · R 지정 N곳」을 보임.

tsc --noEmit 통과 · pytest 427 passed.

남은 것 — **곡선 시작·끝점을 끌어 직선 각도와 R 을 함께 바꾸는 조작**은 다음 단계.
지금은 표시까지이고, 자리 옮기기는 꺾임점 끌기로 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:14:58 +09:00
eomsangdonandClaude Opus 5 f59932de66 feat(B05): 곡선·직선 지우고 더하기 + 반지름 못박기
사용자 지시(2026-09-07) — 「r과 직선 삭제나 추가가 있어야 하지 않을까?」

편집을 **꺾임점 목록 + 자리마다 곡선 켬끔 + 반지름** 셋으로 표현함.
  · 직선 삭제·추가 = 목록에서 점을 빼거나 더하기(빼면 앞뒤 직선이 하나로
    합쳐지고, 직선 위에 더하면 둘로 갈리며 그 자리에 곡선이 생김)
  · 곡선 삭제·추가 = `curve` 를 끄고 켜기(끄면 직선이 그대로 꺾임)
  · 반지름 변경 = `radius_m` 못박기(없으면 서버가 고름)

`POST /route/replan` 의 정점마다 `curve`·`radius_m` 을 받게 함.
편집값이 오면 **묶지 않음** — 사용자가 곡선 하나로 본 것을 임의로 합치면
손잡이가 사라지기 때문. 편집값은 점과 짝이라 중복 제거도 함께 함.

시험 tmp/tests/test_route_polyline_edit.py 7건 — 곡선 삭제·추가, 반지름
못박기, **반지름을 바꿔도 교각점은 안 움직임**(「주변 직선 각도 구속」),
직선 삭제·추가. pytest 427 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:08:47 +09:00
eomsangdonandClaude Opus 5 6b76a3f273 feat(구조물): C군 벽에 「설치 측」 칸 추가 + 칸을 잠글 수 있는 구조
PLAN 3-6 「설치 측을 사용자가 고르게 할지(폼에 칸 추가) 뒤에 정할 것」 — 2026-09-07
사용자 확정 「폼 선택은 가능하게 반영하고 나중에 선택 비활성화로 하자」.

- 레지스트리 C군 5종(옹벽·돌쌓기(찰)·돌쌓기(메)·흙막이·큰돌쌓기)에 `side` 선택 칸.
  선택지 「자동(성토 쪽)·좌·우」, 기본은 자동 — 종전 동작(성토 나는 쪽 자동)과 같음.
- 기하는 이미 그 값을 봄 — `B06_Section_UI_Cross_Revetment.ts:168` 이 "좌"/"우" 면 그쪽,
  아니면 `fillSide()`. 배관(`common_util_structure_walls.ts:80`)·파이썬 짝도 `options.side`
  를 이미 나름. 즉 배선은 있고 **칸만 없었음**.
- 옵션 칸에 `enabled` 를 둠(기본 참). 거짓이면 폼에 **회색으로 남고** 못 고르며 필수 검사
  에서도 빠짐 — 칸을 지우지 않고 잠그는 자리(사용자 「나중에 비활성화」).

자체검증 — 레지스트리 5종 모두 `(['자동(성토 쪽)','좌','우'], 기본 '자동(성토 쪽)', enabled True)`
로 읽힘. 실화면 B06 C군 폼에 「설치 측」 칸이 그 세 선택지로 뜸(값 자동). tsc 통과.
시험 499 통과 — 실패 1·오류 8 은 모두 `require is not defined in ES module scope` 인
JS 도우미 건으로 내 변경과 무관(도우미 파일 날짜 8/27·9/2, 다른 환경에서 만든 것).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:06:12 +09:00
eomsangdonandClaude Opus 5 9626d9e8c7 feat(B05): 곡선 생략 규칙을 쓰지 않음 — 꺾이는 자리에는 늘 곡선
사용자 지시(2026-09-07) — 「내각 155 이상이면 곡선 생략은 반영하지 말자.
사용자가 계획 평면 노선을 수정할 때 문제가 될 것 같아.」

곡선이 있다 없다 하면 잡는 손잡이도 있다 없다 하고, 조금 폈다는 이유로
곡선이 사라지면 되돌릴 길이 없음. 그래서 별표2 의 155° 생략 규칙을 쓰지 않고
꺾이는 자리에는 전부 곡선을 둠. 상수는 법정 근거를 잃지 않게 남겨만 둠.

실측(용화 예상노선 331점) — 곡선 13 → 26곳, 정점 145 → 181,
반지름 12~28 → 12~199m, 내각 109~177°.
벗어남은 그대로 최대 3.57m · 평균 1.081 → 1.102m (원본 충실도 유지).

시험 하나를 뒤집음 — 「펴진 자리는 곡선을 안 둔다」 → 「펴진 자리에도 곡선을
둔다」. 아주 완만하므로 원본과 거의 겹치는 것도 함께 확인.
pytest 420 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:06:00 +09:00
eomsangdonandClaude Opus 5 a811286e52 feat(B06): 구조물 전체를 B06 에서 넣게 (B군 종단배수·F군 생태/녹화 열림)
PLAN 3-6 (2026-09-07 사용자 지시 5 「A군뿐 아니라 구조물 전체를 넣을 수 있어야 함」).

원인은 「B06 이 A군만 받는다」가 아니라 **레지스트리의 `enabled:false`** 였음 — B군 6종·
F군 2종·G군 1종이 목록에서 통째로 빠져 있었고, 두 화면이 같은 목록을 쓰므로 B05·B06 이
똑같이 A/C/D/E/G/기타 여섯 군만 보였음. 레지스트리 주석(2026-08-17)이 그 셋을 「B05
선택지에서 빼고 **B06 개별 횡단도 옵션으로 재사용**」이라 적어 둔 자리임.

- `fetchStructureTypes(includeDisabled)` 로 갈래를 둠. B06 만 참으로 부름 — B05 는 종전대로.
- 함께: 3-5 ⓑ 되돌릴 자리 주석(단 2개 이상일 때만 이어 붙이는 한 줄) 표시.

자체검증 — 공용 브라우저에서 두 화면 군 목록 대조:
  B06 A·**B**·C·D·E·**F**·G·기타 (8) / B05 A·C·D·E·G·기타 (6, 그대로)
  B06 에서 B군 6종(측구·산마루측구·소단측구·도수로·절토사면 배수로·맹암거)과
  F군 2종(야생동물 이동통로·비탈면 녹화)이 실제로 목록에 뜸. tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:57:15 +09:00
eomsangdonandClaude Opus 5 e593378d33 feat(B05): 직선·곡선 성분을 정본에 남기고 화면에 내려보냄
사용자 확정(2026-09-07) — 사용자가 잡는 것은 **곡선 시작·끝점**이고 필요하면
반지름을 직접 바꿈. 정점 목록만으로는 어디부터 어디까지가 한 곡선인지,
그 반지름이 얼마인지 알 수 없어 편집·도면이 같은 값을 못 봄.

  - RouteCurve 신설 — 교각점(apex) · 반지름 · 접선길이 · 내각 ·
    곡선 시작점 · 끝점 · 그 곡선이 대신하는 꺾임점 구간 · 위반.
    반지름을 바꿔도 apex 는 안 움직임(「곡선 반지름 변경 시 주변 직선 각도
    구속」이 그 뜻 — 직선이 고정이라 접선점만 미끄러짐).
  - `planned_route_curves.json` 으로 저장(노드 CSV 옆).
  - `GET /route/plan` 이 `curves` 를 함께 돌려줌.

곁들여 고친 것 — 묶음을 도로 쪼갤 때 **별표2 의 155° 를 다시 안 봐서**
펴진 자리(내각 159°)에도 곡선이 생기고 있었음. 쪼갤 때 다시 검사함.
그 결과 용화 노선이 곡선 24 → 13곳, 정점 179 → 145 로 제자리를 찾음.

실측(용화, 예상노선 331점) — 노드 28 · 곡선 13 · 정점 145 · 위반 0 ·
벗어남 최대 3.57m 평균 1.081m(고치기 전 1.077m). 반지름은 12~28m 로
자리마다 갈림(전에는 전부 12m).

pytest 420 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:53:35 +09:00
eomsangdonandClaude Opus 5 db7004964d feat(B05): 계획노선을 「직선 > 곡선 > 직선」으로 — 반지름을 예정노선에 맞춤
사용자 확정(2026-09-07) — 「계획노선은 직선>곡선>직선 형태의 폴리라인임.
반지름은 법정 최소 값을 지키되 기존 예정노선에 가까운 폴리라인을 찾는 게 키임.」
되물어 확정: 반지름 상한은 두지 않고 원본에 가장 가까운 값으로.

옛 방식은 꺾임점마다 법정 하한(R 12m) 원호를 하나씩 끼웠음. 그래서 완만한
긴 곡선이 작은 원호 여러 개로 쪼개져 원본과 벌어졌음.

고친 것 셋
  1. 이어진 꺾임을 **한 곡선으로 묶음**(_curve_runs). 같은 쪽으로 도는
     꺾임을 모으고, 앞뒤 직선을 늘려 만나는 자리를 그 곡선의 교각점으로 씀.
  2. 그 안에서 **반지름을 골라 맞춤**(_fit_radius_m) — 법정 하한 이상에서
     예정노선 점들과의 평균 벗어남이 가장 작은 값.
  3. 묶는 것이 손해면 **도로 쪼갬**(_split_wide_runs) — 평균이 나빠지거나
     한 자리라도 8m 넘게 벌어지면 낱개로 둠.

별표2 의 155°(곡선 생략) 규칙은 **묶은 뒤 전체 교각**에 적용하도록 자리를
옮김. 조각 하나하나에 적용하면 완만한 곡선이 쪼개졌음(내각 156·138·154°
짜리 세 꺾임이 갈려 가운데 하나만 곡선이 됐음).

실측 — 흉내낸 매끄러운 곡선에서 원본 반지름을 되찾음
  원본 R 25m → 25.1m (최대 벗어남 0.12m)   전에는 R 12·12·12m, 1.66m
  원본 R 40m → 39.8m (0.09m)               전에는 R 12·12·12m, 2.66m
  원본 R 80m → 78.2m (0.44m)               전에는 R 12 다섯 개, 1.31m
실제 노선 3개(격자 탐색이 낸 지그재그)는 중립 — 최대 벗어남 3.57m 그대로,
평균 1.082 → 1.100m. 급한 꺾임에서는 하한이 이미 최선이라 그럼.

시험 tmp/tests/test_route_polyline_curve_fit.py 4건 신설 — 반지름 되찾기,
하한 지키기, 직각은 하한 그대로, 손해면 안 묶기. pytest 420 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:49:31 +09:00
eomsangdonandClaude Opus 5 70b25a5c22 fix(main): 리로드 감시가 정션을 따라 들어가 되감기던 것
보조 창이 원인을 확정함 —
  B07_DesignDetail\openwebcad\node_modules\forest-road-webapp
     → Junction → 저장소 루트 자신 (npm 이 만든 로컬 패키지 링크)
리로더가 B07 을 훑다가 그 정션으로 루트로 되돌아가고, 거기서 venv·storage 로
다시 들어가며 무한히 되감겼음.

실측 — 감시 폴더를 rglob 하면 파일 486,809개 · 한 바퀴 1,045초.
PowerShell 로 세면 config 4개·B07 23개뿐임. 파이썬 rglob 은 정션을 따라
들어가고 PowerShell 은 안 따라가서 눈으로는 안 보였음.

링크(정션) 폴더를 통째로 건너뛰게 함 — 정션이 또 생겨도 재발 안 함.
앞 커밋(e7d30327)이 node_modules 품은 폴더를 빼며 이번 건은 이미 덮었으나
(CPU 85% → 18%), 그건 우연히 막힌 것이라 원인 자체를 막음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:43:48 +09:00
eomsangdonandClaude Opus 5 e7d3032727 perf(main): 한 겹 아래 숨은 node_modules 도 리로드 감시에서 뺌
앞 커밋이 바로 밑의 node_modules 만 봐서 새는 곳이 있었음 —
`B07_DesignDetail/openwebcad/node_modules`(15,944개)와
`A00_Common/config/node_modules`. 그 폴더들이 0.25초마다 통째로 훑히고 있었음.

한 겹 아래(`*/node_modules`)까지 보고 제외.

실측 (놀고 있는 서버, 같은 기기·같은 절차) — **CPU 85.2% → 18.3%**.
감시 대상은 파이썬 143개 · 하위폴더 39개로 줄었음.

⚠ 원인 규명은 보조 창 몫이 컸음 — 프로세스 역할이 뒤바뀌어 보였음(내가
띄운 것은 껍데기, 그 자식이 리로더, 손자가 실제 서버). 부모만 보고 재면
0% 로 보여 「앱이 먹는다」로 두 번 오진했음. 갈래 실험(reload 끔 0% /
켬 64%)이 그것을 갈랐음.

남은 18% 는 감시 탓으로 보기 어려움(파일 143개) — 별건으로 남김.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:35:53 +09:00
eomsangdonandClaude Opus 5 e9ad8cdfaf fix(B06): 다단 기슭막이 성토부선을 면적 트림에 넣음 — 단 수가 물량에 반영
PLAN 3-5 ⓑ (2026-09-07 사용자 확정 「옹벽도 수량이 변경되어야 함」).

그동안 면적 폐회로의 바깥 경계가 **기준벽 사면(노견~벽 상단)** 까지만이었고 그 바깥은
원지반으로 봐서 면적 0 이었음. 그래서 단을 몇 개 올려도 절·성토가 한 자리도 안 변했음.

- 화면이 그리는 선(`outletFill.segments`·`basinFill.segments`)을 트림 사면에 이어 붙임.
  「면적은 실제로 그려지는 설계선을 따른다」는 기존 원칙과 같은 자리로 맞춘 것.
- 그리지 않는 `cut` 갈래(벽이 원지반에 묻혀 성토가 없는 자리)는 뺌 — 화면과 같은 규칙.

실측(용화 route 165, 새 트림 대 옛 트림):
  81.77m  0단 성토 8.4247 → 10.9027(+2.478) · 1단 이상 10.6743(+2.250) · 절토 불변
  244.14m 지형이 단을 거부(0단) · 성토 3.3492 → 4.0511(+0.702) · 절토 불변
  423.55m 벽이 묻힌 자리(cut) · 성토 3.6145 그대로 · 변화 없음
→ 단이 서면 성토가 줄고(10.90 → 10.67), 단이 안 서는 자리도 **벽 바깥 성토가 이제
  잡힘**. 후자는 단과 무관한 변화라 보고에 따로 적었음.

파이썬 짝은 없음 — 구조물 트림 면적은 TS 한 벌이고 서버는 그것을 Node 로 돌림
(`B06_Section_Engine_Areas.py` 는 구조물 없는 표준 계산). tsc 통과·시험 500 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:25:28 +09:00
eomsangdon ff9bcdf68d Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 07:18:47 +09:00
eomsangdonandClaude Opus 5 7a1d7db68f perf(main): 리로드 감시에서 node_modules 를 품은 폴더도 뺌
앞 커밋(351db312)이 storage·venv 는 뺐으나 `config` 가 남아 있었음.
그 안에 `node_modules` 가 있어 0.25초마다 의존성 트리를 훑고 있었음.

`node_modules` 를 품은 폴더는 감시에서 제외. 그 결과 `config/` 를 고칠 때는
`main.py` 와 마찬가지로 서버를 손으로 다시 띄워야 함 — 둘 다 자주 고치는
자리가 아님.

⚠ 측정 메모 — 루트 전체 훑기는 이 기기에서 **300초에도 한 줄을 못 뱉었음**
(옛 방식이 0.25초 주기를 지킬 수 없었다는 뜻). 다만 지금 기기가 계속
눌려 있어(서버 둘이 놀면서도 각 70%) 정확한 전후 수치는 못 냈음.
그 CPU 자체는 별건으로 계획서에 남겨 둠.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:12:15 +09:00
eomsangdon d0074141f8 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 06:39:55 +09:00
eomsangdonandClaude Opus 5 351db312ee perf(main): 자동 리로드가 훑는 폴더를 소스만으로 좁힘
uvicorn StatReload 는 0.25초마다 지정 폴더를 rglob("*.py") 로 재귀로 훑는데,
폴더를 안 주면 현재 폴더가 대상이라 storage(27GB) · venv(파이썬 파일만
7,530개) · node_modules 까지 통째로 돌고 있었음.

파이썬이 실제로 사는 폴더 14개만 감시하게 함(산출물·의존성·백업 제외).
루트는 넣지 않음 — 넣으면 재귀라 도로 다 훑음. 그 대신 main.py 를 고칠 때는
서버를 손으로 다시 띄워야 함.

⚠ 이 변경이 「요청 없는 서버가 CPU 70%」를 고치지는 못했음. 그 CPU 는
감시 프로세스(0%)가 아니라 **앱 프로세스** 쪽이며 원인 미상 — 계획서에
남겼음. 이 커밋은 감시 낭비만 걷어낸 것임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 06:30:37 +09:00
eomsangdonandClaude Opus 5 1553f23c0d perf(B04): 등고선 피처 풀기를 한 번만 (격자와 무관)
PLAN 0-10. 「등고선 굽기」가 확장 8회 합 19s 였는데, 실측해 보니 굽기 자체는 0.8~2.0s
이고 나머지는 **피처 4,200개를 회차마다 다시 `shape()` 로 푸는 값**이었음. 푸는 결과는
격자와 무관하므로 같은 목록·같은 하한이면 그대로 쓴다(목록 객체를 함께 들고 있어 id 가
다른 목록에 재사용되지 않음, 최근 2벌만 보관).

실측 — 같은 격자 연속 4회: 2.66 · 1.90 · 1.99 · 2.03s (2회차부터 파싱 재사용), 결과
배열은 4회 모두 동일. `build_contour_descent` 전체는 옛 코드 12.8s → 7.2s 이며
band_elevation·valid·receiver·step_length·azimuth·levels 전부 `np.array_equal` 동일.

안 한 것 — 격자가 커질 때 옛 굽기를 옮겨 붙이고 **테두리만** 굽는 안은 만들어 재 봤으나
`rasterize` 호출당 고정 비용 때문에 오히려 3.2s → 10.6s 로 느렸고, 창에 걸치는 선만
넘기도록 고쳐도 3.8s 로 손해였음(창 4개 × 166단). 그래서 걷어내고 파싱 캐시만 남김.
`levels` 는 피처 전체에서 나오므로 격자와 무관하다는 것도 확인함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 05:48:38 +09:00
eomsangdonandClaude Opus 5 f1834465c8 perf(B04): 창이 모자라면 그 타일만 넓혀 다시 재도록
PLAN 0-10 이어짐. 첫 판에서는 창이 모자란 단계를 **격자 전체**로 되돌렸는데, 격자가
커질수록 그 폴백이 25/91 까지 늘어 이득이 대부분 사라졌음(실측 확장 97.7s).

- 모자란 타일만 창을 4배로 넓혀 다시 잼. 단계 전체를 되돌리지 않음.
- 타일 256 → 128 (실측: 128/여유32 2.1s vs 256/여유32 2.7s).
- 넓힘 횟수를 계측 줄에 남김(폴백 대신).

자체검증 — 전체격자 5.2s → 2.1s(넓힘 15/91), 거리·목표행·목표열 전부 동일.
`build_contour_descent` 결과도 옛 코드와 완전 동일(8.0s → 5.1s). 시험 500 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 05:29:18 +09:00
eomsangdon c90425db72 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 05:21:01 +09:00
eomsangdonandClaude Opus 5 56d5fb26b8 perf(B04): 밴드별 하강거리 EDT 를 타일 창으로 좁힘 (값 보존)
PLAN 0-10. 확장 95.8s 중 밴드별 EDT 가 50.4s 였음 — 단계마다 격자 전체를 훑는데
정작 쓰는 값은 그 단계 밴드 셀뿐임.

- 격자를 256칸 타일로 나눠 **셀이 있는 타일만** 둘레 32칸까지 잘라 EDT 실행.
  `analyze_domain` 이 domain 을 안 넘겨 밴드가 전역에 흩어지므로 바운딩박스 하나로는
  안 좁아짐 — 타일이라야 좁아짐.
- 창 안 최대 거리가 여유에 닿거나 창에 낮은 라인이 없으면 **그 단계를 전체 격자로 다시**
  — 근사가 아니라 같은 값을 싸게 구하는 것. 폴백 횟수는 계측 줄에 찍음.
- `return_indices` 가 주는 창 좌표에 창 원점을 더해 전체 격자 좌표로 되돌림.

자체검증 — 실제 자료(745×1035·166단, EDT 도는 91단)에서 전체격자 10.8s → **2.9s**,
폴백 9/91, 거리·목표행·목표열 **전부 `np.array_equal` 동일**. `build_contour_descent`
결과(band_elevation·valid·receiver·step_length·azimuth·levels)도 옛 코드와 완전 동일.
여유·타일 선택 근거는 파일 주석의 실측표 참조. 시험 500 통과·18 건너뜀
(기존 실패 8건은 다른 환경의 JS 도우미 문제로 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 05:18:35 +09:00
eomsangdonandClaude Opus 5 1944414940 fix(B05): [확인]을 누를 때마다 계획노선이 깎이던 것
같은 노선으로 [확인]을 되풀이하면 계획노선 정점이 136 → 134 → 119, 노드가
22 → 21 로 계속 줄었음(보조 창 실측). 사용자가 아무것도 안 옮겨도 누를
때마다 자기 노선이 뭉개졌음.

원인은 **이미 폴리라인인 것을 다시 단순화**한 것. 두 자리 모두 그랬음 —
  - read_route_plan 이 노드를 계획노선(원호 점이 섞인 폴리라인)에서 되뽑음
  - replan_route 가 화면이 보낸 노드를 또 한 번 단순화해서 씀
단순화(Douglas-Peucker)는 원본 점군에서 한 번만 돌아야 함.

고친 것:
  - build_planned_polyline 에 simplify 갈래 신설. False 면 받은 점을
    꺾임점으로 그대로 씀(중복 제거만).
  - 폴리라인을 쓸 때 그것을 낳은 **노드도 함께 저장**
    (planned_route_nodes.csv · planned_route_initial_nodes.csv).
    노드는 폴리라인에서 되뽑을 수 없으므로 낳은 값을 보관함.
  - read_route_plan 은 저장된 노드를 그대로 씀(없는 옛 프로젝트만 뽑음).
  - replan_route 는 simplify=False 로 씀.

실측(용화 b269ea34 실제 노선 파일, 4회 왕복)
  옛 방식  노드21/정점115 → 20/115 → 20/113 → 20/115  (깎이고 흔들림)
  고친 방식 노드21/정점115 → 이후 매 회차 **완전히 동일**

시험 tmp/tests/test_route_polyline_idempotent.py 2건 신설 — 노드 왕복이
제자리인지, 그리고 옛 방식이 실제로 깎이는지(갈래가 필요한 이유) 못박음.
pytest 414 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 05:08:07 +09:00
eomsangdonandClaude Opus 5 5c22c367a9 perf(계측): 격자 확장·하강 방향장 단계 마크 추가
PLAN 0-10. 1차 영역이 2.6초로 떨어진 뒤 남은 병목이 `expand_by_red_boundary` 102초라
그 안을 가르기 위한 계측. 로직 불변.

- 확장 회차별 시간·격자·셀 수·방향장 재사용 여부를 한 줄씩
- 격자 해석 1회: 하강 방향장(재사용/새로) / 지형 조립 / 도로 굽기 / 세류 새김 / 도달 판정
- 하강 방향장: 등고선 굽기 / 밴드 표고(EDT 1회) / 밴드별 하강거리(EDT N회) /
  위치에너지 조립 / 흐름 경로

실측 로그상 방향장이 회차마다 새로 만들어지고 밴드가 100단이라, 격자 전체 EDT 가
회차당 100회씩 도는 구조로 보임 — 그 가설을 수치로 가르려는 것.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 04:58:45 +09:00
eomsangdonandClaude Opus 5 58bfe72ab5 chore(B04): 표고를 TIN 밖에서 읽은 점 수를 로그로 남김
보간 구름을 물어볼 자리 둘레 100m 로 좁힌 뒤(9317ad31), 그 둘레에
등고선이 없는 자리는 볼록껍질 밖이라 최근접 정점 값으로 빠짐. 그 값은
등고선 간격(5m)만큼 틀릴 수 있고 그러면 그 조각의 상·하류 판정이 갈림.

용화 자료에서는 안 일어났으나(판정값 일치) 다른 현장에서 조용히 갈릴 수
있어 몇 점이 빠졌는지 한 줄로 남김. 계산은 안 바뀜.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 04:58:08 +09:00
eomsangdon 9ded6fe6c6 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 04:57:03 +09:00
eomsangdon c9cd670315 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 04:56:24 +09:00
eomsangdonandClaude Opus 5 b6941bd28d fix(B06): 재계산이 다단 구간값을 지우던 것 — 사용자 조작값 목록을 한 벌로
같은 「사용자 조작값」 목록이 세 곳에 흩어져 있었고 서로 달랐음.
브라우저(B06_Section_Cross_Refresh.ts PRESERVED_KEYS)는 extra_spans 를
살렸는데 서버 두 곳은 안 살렸음 —

  - B06_Section_Router_Design._USER_TOUCHED_KEYS (포장 강제 재계산 ·
    세월교 노면 하강 재계산이 씀)
  - B06_Section_Router 안의 인라인 사본 (단측점 설계 갱신)

그래서 포장 구간이나 세월교가 앉은 측점이 다시 계산되면 사용자가 넣은
다단 구간값이 조용히 사라졌음.

_USER_TOUCHED_KEYS 를 공개 이름 USER_TOUCHED_KEYS 로 바꾸고 extra_spans 를
넣음. 라우터의 인라인 사본을 지우고 그 한 벌을 씀.

시험 tmp/tests/test_b06_user_touched_keys.py 3건 신설 — 서버 목록에
extra_spans 가 있는지, 서버와 브라우저 목록이 같은지(status ·
pavement_suggested 는 부르는 쪽이 붙이므로 제외), 라우터에 사본이
남지 않았는지. 목록이 다시 갈라지면 시험이 깨짐.

pytest 412 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 04:50:43 +09:00
eomsangdonandClaude Opus 5 48c856ea8d fix(계측): B06 확정 마크가 다른 함수에 정의되던 것 바로잡음
`marks` 정의가 `save_sections` 에 들어가고 사용은 `confirm_sections` 에 남아
`NameError: name 'marks' is not defined` 로 [확인] 재계산이 500 으로 끝났음
(노선은 자동 롤백돼 자료 손상 없음). 정의를 쓰는 함수 안으로 옮기고
`save_sections` 의 잘못 들어간 두 줄 제거. 계측 항목은 그대로.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 04:50:08 +09:00
eomsangdonandClaude Opus 5 9317ad3175 perf(B04): 표고 보간 삼각망을 물어볼 자리 둘레로 좁힘
노선 [확인] 이 293초였고 그중 285초가 배수유역 분석, 다시 그중 256초가
1차 영역 만들기였음. 범인은 ElevationSampler 하나임 —
LinearNDInterpolator 가 들로네 삼각망을 첫 호출 때 만드는데 등고선 구름을
통째로(용화 317,348점) 넘겨 그 한 번이 256~1,481초로 뛰었음(편차 큼).

표고를 묻는 자리는 도로 교차 노드에 닿은 세류 조각 몇 개뿐임. 그 조각들만
먼저 골라 그 둘레 100m 안 등고선만 남기고 삼각망을 만듦. 여유 100m 는
등고선 재샘플 간격(5m)의 스무 배라 물어볼 점은 언제나 볼록껍질 안쪽이고
보간값이 그대로임.

실측(용화 5601e828) — 보간 구름 317,348 → 4,367점,
1차 영역 256.6s → 43.8s(측정 중 기기 부하가 9배였음, 준비 5.1s → 45.7s).
결과 완전 동일 — 1차 영역 266,697㎡ · 격자 545×835 · 268,456셀 ·
노선 이탈 0m/1079m · 상류망 1 / 하류망 109 / 미연결 1153.

pytest 409 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 04:37:00 +09:00
eomsangdonandClaude Opus 5 4da04929c4 perf(계측): 재확정 체인 병목 추적용 단계 마크 추가
PLAN 0-10. 8000 에서 1회 실측 결과 재확정 체인 292.9s 중 배수유역 분석이 285.0s(97%)로
나와, 그 안쪽을 가르기 위한 마크만 추가. 로직 불변.

- B06 확정: 조회 / 기본설계 계산 / 확정 저장 / 서버 재계산 / 측구 역반영
- 서버 재계산 내부: 상세+DB 병렬 조회 / 포장·세월교 보정 / Node 번들 / 정본 저장
- 배수유역 분석 내부: _prepare(도엽 읽기·좌표변환) / preview_stages / 응답 만들기·저장
- preview_stages: 1차 영역 / 격자 확장·흐름 판정 / 흐름 강도 / 외곽선·기본 관·화살표

`_log_steps` 는 체인 모듈 것을 그대로 씀(지연 임포트로 순환 회피).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 03:20:39 +09:00
eomsangdonandClaude Opus 5 41852da3b3 feat(B03): 초기 업로드부터 계획노선 폴리라인을 노선 정본으로 사용
자동 체인 2.7 단계 추가 — 초기 폴리라인을 세운 뒤 노선을 다시 읽어
그것을 BP·경유점·EP 로 세우고 algorithm=as_planned 로 넘김.

전에는 폴리라인 파일만 만들고 노선은 원본 점군으로 풀었음. 그래서
종횡단 측점·유토곡선·3D 코리도가 곡선 없는 점군 위에 섰음.
실측(용화 3건): DB 노선 정점 331·323 vs 초기 폴리라인 145.
노선을 한 번이라도 편집한 프로젝트만 145 로 맞아 있었음.

자체검증 — 2.7 재판독이 145 정점을 돌려주고 BP·EP 좌표가
expected_route.csv 첫·끝 줄과 같음. pytest 409 passed.

계측 로그(_log_steps) 동봉 — 재확정 체인 단계별 경과시간용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 02:56:45 +09:00
eomsangdon c52609417b Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 02:06:54 +09:00
eomsangdonandClaude Opus 5 71694163de fix(B06): 벽을 안 그리는 카드에 [연동] 버튼이 뜨던 것
링크 카드는 소유 측점의 벽을 빌려 그리는데, 빌릴 벽이 없는 카드에도 버튼이 떠 눌러도
아무 일이 없었음 — revetlink 에 detached 만 쌓였음(보조 창 실측: 0·20·40·60·80m 카드).
linkState 가 drawnWallKeys().length 도 함께 보게 함.

계획서 3-4 실화면 확인도 함께 마침(공용 브라우저, 용화):
- 벽이 있고 연동이 켜진 카드 820.00m 에서 [연동]을 끄자 revetx 에 820.00:outlet h=1.8 이
  적힘 — 푸는 순간의 높이가 그 측점 값으로 굳음.
- 그 뒤 소유 측점 821.62m 높이를 4.2m 로 올려도 820.00m 은 1.8m 그대로 — 따로 놂.
※ 앞서 안 굳는 것으로 본 것은 측정 오류였음. 패널이 카드마다 하나씩 67개인데 첫 번째
   패널만 봤고, 벽이 없는 카드를 골랐음(보조 창이 가려냄).

시험 409 통과·17 건너뜀, typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:37:59 +09:00
eomsangdonandClaude Opus 5 fe14ad02ee feat(B05): 계획노선 초기 폴리라인을 자동설계 체인에서 세움
PLAN 0-10. 그동안은 화면이 route/plan 을 처음 부를 때 만들어져, 화면을 안 열면 값이
없었음 — 초기값은 서버가 낸다는 0-4 원칙과 어긋났음.

- 파일입력 자동설계 체인이 예상노선 정본을 남긴 직후(2.6단계) 초기 폴리라인도 세움.
  실패는 비치명적 — 없으면 화면이 처음 열릴 때 만들어짐.
- _ensure_planned_initial 이 **낡음도 본다** — 예상노선 파일이 더 나중에 쓰였으면 다시
  만듦. 파일을 다시 올리면 예상노선이 새로 깔리는데 초기본이 옛 노선인 채로 남으면
  노선 초기화가 옛 자리로 돌아갔음.

자체검증 — 예상노선 파일 시각을 최신으로 만든 뒤 route/plan 을 부르니 초기 폴리라인이
다시 만들어짐(노드 28 · 곡선 13 그대로). 시험 409 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:19:28 +09:00
eomsangdonandClaude Opus 5 710169fd00 fix(B05): [3D 업데이트]가 만든 코리도를 바로 올리고 보관함에도 담음
보조 창 지적 — [3D 업데이트]를 눌러도 브라우저가 만들기만 하고 PUT 이 없었음.
저장 시점이 B05->B06 이동뿐이라(2026-08-23 확정), 누른 뒤 새로고침하면 저장본이 낡은
채라 다음 사람이 또 만들어야 했음.

- 버튼 경로 끝에서 saveCorridorIfDirty 를 부름.
- saveCorridorIfDirty 가 PUT 성공 뒤 그 봉투를 브라우저 보관함에도 담음(옛 열쇠 자리는
  치움). 그러지 않으면 다음 진입이 새 열쇠 주소로 16.9MB 를 한 번 더 받았음.

typecheck 통과. 실측 확인은 보조 창이 자기 프로젝트에서(저장본이 최신인 자리) 진행.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:09:58 +09:00
eomsangdon 74a631182d Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 01:05:30 +09:00
eomsangdonandClaude Opus 5 908c75d535 fix(B05): 노선 변경 때 옛 사용자 설계가 덮여 사라지던 것 — 이월 순서
보조 창 실측 — 노드를 옮겨 재계산하면 다단 구간값(extra_spans)이 0건이 됐음.
심어 둔 표식(85.18m 의 14/6/8)이 통째로 사라짐.

원인은 순서였음. 이월이 _prepare_drainage_pipes_and_reprofile(refresh=True) **앞**에서
돌았는데, 그 단계가 측점을 새 노선 기준으로 다시 만들며 방금 얹은 설계를 덮었음.

- 이월을 측점 재생성 **뒤**로 옮김. 옛 설계는 이미 old_designs 로 손에 있음.
- 행마다 쓰던 것을 merge_cross_section_designs 로 묶음 — 행 수와 무관하게 왕복 두 번
  (원격 DB 왕복 약 12ms/질의).

같은 요청에서 확인된 통과분(보조 창) — 측점 수·끝 누가거리가 새 노선과 일치(67·1,097.4m
고정 -> 64·1,079.4m), 배수관 측점 0 -> 7~9, 「세그먼트 경로 탐색 실패」 로그 사라짐.

시험 409 통과·17 건너뜀, main import 스모크 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:04:49 +09:00
eomsangdon 735d34b743 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 00:49:44 +09:00
eomsangdonandClaude Opus 5 674ca82726 feat(B05): 고친 계획노선을 그대로 쓰는 입구 — 재확정 체인이 다시 풀지 않음
PLAN 0-2 항목 9 / 0-3. 노선 편집 [확인]이 재확정 체인을 타는데 그 체인이 solve_route 로
BP·CP·EP 사이를 격자에서 다시 풀었음. 사용자가 노드를 조금만 비틀어도 탐색 제약에 걸려
체인이 통째로 멈췄음(보조 창 로그: 세그먼트 1 (BP -> CP1) 경로 탐색 실패 — 종단경사
한계 26% · 최소곡선반지름 12m · 회피지역 제약).

- B05_Profile_Engine_AsPlanned.solve_as_planned 신설 — 제어점 목록이 곧 노선이고 표고만
  지표면 격자에서 뜸. 반환 꼴은 솔버와 같아 아래 단계가 그대로 이어짐.
  종단기울기·곡선반지름 위반은 **세되 막지 않음**(사용자 확정: 자동 보정·차단 없이 경고만).
- run_route_design 에 algorithm=as_planned 갈래 추가. 스키마 검증에도 허용.
- 재확정 체인이 그 갈래를 씀 — 자동탐색은 이제 초기 업로드에서만 돎.

솔버와 지표 계산이 겹치지만 합치지 않음 — 솔버는 0-3 으로 접히는 코드라 리팩터링해
두 곳을 얽을 값어치가 없음.

시험 409 통과·17 건너뜀, main import 스모크 통과.
실화면 [확인] 재검증은 보조 창이 자기 프로젝트에서 진행.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:49:07 +09:00
eomsangdon b1be5ade7c Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 00:45:09 +09:00
eomsangdonandClaude Opus 5 53525a1d91 fix(B05): 노선 재계산이 끊겼을 때 노선만 갈아 끼워진 채 굳던 것
보조 창 실측 — [확인] 이 체인 중간에 400 으로 끊겨도 route/replan 은 success 를 돌려주고
계획노선 CSV 는 이미 새 노선으로 바뀐 뒤였음. 그 결과 배수유역·관은 새 노선, 종횡단은
옛 노선으로 어긋난 채 굳고(배수관 측점 9 -> 0), 다시 누를수록 어긋남이 쌓였음
(1,097.4 -> 1,079.4 -> 1,064.7m).

- run_redesign_chain 이 중단 사유를 문자열로 돌려줌(성공은 None). 중단 지점 넷 모두.
- replan 이 쓰기 전에 이전 수정본을 쥐고 있다가 실패하면 되돌림. 처음 고치는 경우였으면
  파일을 지워 초기 폴리라인으로 돌아감.
- 실패는 409 + 사유로 응답 — 화면이 성공으로 보이던 것을 막음.

원인 자체(자동탐색이 사용자 노선을 다시 풀다 실패)는 별건으로 남음 — 로그 사유
「세그먼트 1 (BP -> CP1) 경로 탐색 실패: 종단경사 한계(26%)·최소곡선반지름(12m)·회피지역
제약으로 통과 경로가 없습니다」. 고친 노선을 그대로 받아 쓰는 입구가 필요함(PLAN 0-2 항목 9).

시험 409 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:44:29 +09:00
eomsangdon e875cdb2df Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 00:38:36 +09:00
eomsangdonandClaude Opus 5 eec29a90df fix(B05,B06): 최소곡선반지름 조회가 없는 컬럼을 읽던 것 · 재계산 중단 사유 로그
보조 창이 노선 편집 [확인] 실측에서 둘을 잡음.

1. projects 에 design_speed_kph / terrain_type 컬럼이 없음(있는 것은 road_type 뿐).
   매 요청마다 OperationalError(1054) 가 ERROR 로그로 남고 기본값 폴백으로 넘어갔음.
   - 임도 종류는 projects.road_type, 설계속도·지형은 워크플로 stage 2 params 에서 읽음.
   - 산식은 이미 있던 B05_Profile_Engine_Grade.legal_plan_radius_min_m 를 씀 —
     내가 같은 표를 다시 짜 두었던 것을 지움(중복 제거).
2. 재확정 체인이 solve_route 400 으로 끊길 때 상태 코드만 남겨 원인을 못 짚었음.
   본문(사유)까지 로그에 남김. 노선 편집 [확인]이 조용히 끊겨 배수유역·관은 새 노선으로
   가고 종횡단만 옛 노선에 남는 어긋남이 굳었던 자리임.

recompute 의 상세 조회와 DB 두 건도 asyncio.gather 로 묶음(원격 DB 왕복 약 12ms/질의).

시험 409 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:37:53 +09:00
eomsangdon f24d8a34cf Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 00:19:20 +09:00
eomsangdonandClaude Opus 5 e2cc5e759b perf(B05): 3D 코리도를 브라우저 보관함(IndexedDB)에 담아 재수신 없앰
손 안 댄 프로젝트를 열면 저장본 열쇠가 맞아 매번 16.9MB 를 받았고, 브라우저 보관이
모듈 Map 이라 새로고침 한 번에 날아가 또 받았음(보조 창 실측: 첫 로그인~B05 도달 29MB
중 16.9MB 가 이것). 해시 질의(0b30f5b0)는 낡았을 때만 막았지 맞을 때는 그대로였음.

- b_asset_cache 에 readCachedBytes / writeCachedBytes / purgeAssetsWithPrefix 추가.
  앞 둘은 네트워크를 안 타는 순수 보관함 접근이고, 마지막은 주소 앞머리가 같은 옛
  보관본을 지움 — 코리도는 주소에 열쇠가 박혀 정본이 바뀌면 새 주소가 되므로 17MB 짜리가
  쌓이지 않게 담기 전에 치움.
- 코리도 조회가 보관함을 먼저 봄. 주소에 열쇠가 있어 정본이 바뀌면 저절로 다른 주소가
  되므로 옛 보관본을 잘못 쓸 일이 없음.

자체검증(공용 브라우저) — 담기·읽기 왕복 정상(44바이트 왕복, 해시 보존), 열쇠만 다른 두 벌을
담은 뒤 앞머리 지우기로 둘 다 제거 확인. typecheck 통과.
※ 실제 16.9MB 절감은 저장본이 최신인 프로젝트에서 확인해야 함 — 이 프로젝트는 검증 중
계획고를 만져 저장본이 낡은 상태라 코리도 자체가 안 실림(__corridorSource null).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:18:37 +09:00
eomsangdon b566c67c52 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-07 00:10:33 +09:00
eomsangdonandClaude Opus 5 9b93d3055b feat(B05): 노선 편집 모달이 정점 대신 노드를 잡게 함
사용자 지시(2026-09-06) — 노드를 제어해 계획노선을 고친다. 그동안 모달은 서버가 준
폴리라인 정점을 그대로 잡았는데, 거기에는 원호 위 점이 섞여 있어 편집 대상이 아님.

- 그려 보이는 선(폴리라인, 원호 포함)과 잡는 점(노드)을 나눔. 선은 plannedLine,
  노드는 서버가 내려 준 nodes.
- 노드에 붙은 반지름·내각·법정 위반을 화면에 실음 — 위반 노드는 붉게, 상태줄에
  곡선 수와 기준 R, 미달 개수.
- 노드를 옮기면 폴리라인은 낡은 값이므로 지우고 직선으로 미리 보임. 곡선은 [확인] 때
  서버가 같은 R 규칙으로 다시 끼움(계산을 두 벌로 짜지 않음).
- API 타입에 nodes·min_radius_m·curve_count·violation_count 추가.

자체검증(공용 브라우저, 용화) — 모달이 노드 28개로 열리고 상태줄에
「노드 28개 · 초기 폴리라인 · 곡선 13곳(R 12m)」. 전에는 폴리라인 정점 142개를 잡았음.
typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:09:49 +09:00
eomsangdonandClaude Opus 5 00decc812d perf(B05): 계획노선 노드 뽑기 — 완만한 긴 굴곡이 삼켜지던 것 보완
Douglas-Peucker 허용오차는 절대 거리라 굴곡이 완만하고 길수록 통째로 삼켜짐.
4.5km 노선에서 노드 20개(간격 238m)만 남았음(보조 창 실측 5개 표본).

- 남은 노드 사이가 100m 를 넘으면 그 구간만 허용오차를 절반으로 낮춰 다시 뽑음
  (최대 3겹: 4 -> 2 -> 1 -> 0.5m). 노선 길이로 허용오차를 바꾸면 짧고 급한 굴곡이 다시
  뭉개지므로 벌어진 구간만 고름.
- shapely 대신 색인을 돌려주는 Douglas-Peucker 를 직접 둠 — 되뽑기가 원본 구간을 다시
  꺼내야 해서 색인이 필요함. 새 점은 만들지 않음(노드는 언제나 원본 자리).

반지름을 원본 굴곡에서 재어 키우는 방법도 시험했으나 되돌림 — 직각 꺾임에서 R 70m ·
중앙종거 20m 가 나와 원본과 크게 어긋났음. 중앙종거 M = R(1/cos(delta/2) - 1) 이라
R 이 클수록 원호가 꺾임점에서 멀어짐. 법정 하한이 사용자가 지시한 기준값이자 원본에
가장 가까운 값이므로 그대로 둠. 곡선별 R 조정은 노드 편집 기능으로 분리(PLAN 0-10).

자체검증 — 1.1km 노선 3개: 노드 25 -> 28, 곡선 13, 위반 0, 최대 간격 82m.
4.5km 노선: 노드 20 -> 24. 그 노선의 최대 간격 1,300m 는 알고리즘 문제가 아니라 실제
직선 구간임을 확인(원본 점 간격 중앙 3.3m · 최대 3m 로 고르고, 내각 최소가 158°).
시험 2건 추가(완만한 굴곡이 살아나는지 · 되뽑기가 원본 점만 남기는지). 전체 409 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:05:48 +09:00
eomsangdonandClaude Opus 5 318507c88d fix(B05,B06): 종단 알약으로 고른 구조물이 B06 횡단도에 안 잡히던 것
계획서 2-2. 3D 픽·사이드 목록에는 structure-pick 기록이 있는데 종단 그래프 알약 경로에만
빠져 있었음 — 세션 키가 아예 안 써져 B06 이 잡을 값이 없었음(보조 창 추적).

- 구조물 브리지에 markChainage 추가 — 알약 id 의 누가거리를 구조물 정본·관 정본 양쪽에서
  되읽음.
- Page 의 onStructureSelect 가 writeStructurePick 으로 그 자리를 남김(사이드 목록 경로와
  같은 창구).

자체검증(공용 브라우저, 용화) — 알약 18개 중 셋을 눌러 세션 키가
찰쌓기 1 -> {at:40} · 찰쌓기 3 -> {at:80} · 옹벽 -> {at:120} 으로 써짐(전에는 null).
그 상태로 B06 진입 시 측점 6+0.0(120m) 카드가 선택된 채로 뜸. typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:58:53 +09:00
eomsangdon 8584a53967 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 23:55:12 +09:00
eomsangdonandClaude Opus 5 356b32163a feat(B05): 계획노선을 폴리라인으로 만드는 초기본 신설
사용자 확인(2026-09-06) — 예상노선은 폴리라인이 아니라 점 묶음이고 규칙 없는
폴리라인과도 맞지 않음. 그래서 계획노선은 원데이터를 복사해 폴리라인으로 바꾼 것이어야
하고 그것이 불변의 초기 데이터가 됨. 곡선 기준은 지식DB(별표2 I.2.다) 값을 씀.

- common_util_route_polyline.py 신설 — 점 묶음을 폴리라인으로.
  ① Douglas-Peucker 로 꺾임점(IP)만 남김. 예상노선은 격자 탐색이 낸 조밀한 점군이라
     (용화: 1,097m 에 331점) 점마다 곡선을 끼우면 접선 자리가 1.5m 뿐이라 R 이 2~6m 로
     뭉개짐. 허용오차는 격자 해상도(2m)의 두 배.
  ② 내각 155도 이상은 곡선 생략(별표2), 그 아래는 최소곡선반지름으로 원호를 끼움.
     자리가 모자라면 반지름을 줄이되 막지 않고 위반으로 표시(사용자 확정: 경고만).
  ③ 점은 옮기지 않음 — 노드는 원본 자리에 그대로 두고 그 사이에 원호를 넣음.
- planned_route_initial_path 신설 — 세 벌 구조(예상노선 점 묶음 / 초기 폴리라인 /
  수정본). load_design_route 읽는 순서에 초기 폴리라인을 예상노선보다 앞에 끼움.
- 최소곡선반지름은 임도 종류·설계속도·지형으로 고름
  (FOREST_ROAD_PROFILE_CRITERIA[min_plan_radius_m], 못 읽으면 가장 완화된 조건).
- GET /route/plan 이 노드(반지름·내각·위반)까지 함께 돌려줌 — 노드만 옮기면 선이
  저절로 규칙을 지키게 하는 것이 목적. POST /route/replan 도 받은 노드로 다시 폴리라인화.

자체검증(용화, 실화면 API) — 예상노선 331점 -> 편집 노드 25개 + 곡선 13곳
(전부 R 12m = 설계속도 20·특수지형 법정 하한, 위반 0) -> 계획노선 정점 142개. 응답 180ms.
단순화 전에는 곡선 15곳이 R 2.75~6.51m 로 전부 위반이었음.
시험 tmp/tests/test_route_polyline.py 7건(원호가 접선과 맞물리는지 좌표로 대조).
전체 407 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:53:25 +09:00
eomsangdonandClaude Opus 5 e5b896283f fix(B05): 배분 선반입 되먹임을 응답 반영 구간에서 끊음
표식(측점 수 + 누계)만으로는 부족했음 — 응답을 반영해 다시 그린 뒤 누계가 아주 조금
달라지는 경로가 있어 편집이 멈춘 뒤에도 2~3바퀴가 더 돌았음(보조 창 10초 관찰:
2.8~6.3초에 걸쳐 3건).

onReady 로 다시 그리는 동안 applying 을 세워 그 그리기가 거는 schedule 을 무시함.

자체검증(공용 브라우저, 접은 채 계획고 6회 편집 후 10초 관찰 3회) — 3/3/1건 -> 2/1/1건.
편집하는 동안은 종전대로 0건(400ms 안정화 유지). typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:27:37 +09:00
eomsangdon f7e12dddf1 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 23:13:56 +09:00
eomsangdonandClaude Opus 5 c8468e789d fix(B05): 유토 배분 선반입이 접힌 상태에서 안 돌던 것 · 응답이 요청을 다시 부르던 되먹임
- schedule 호출이 draw 의 접힘 가드 뒤에 있어 패널을 접어 두면 한 번도 안 불렸음.
  그래서 「편집이 멈추면 조용히 받아 둔다」가 성립하지 않고 펼친 뒤에야 첫 요청이 나갔음
  (보조 창 실측: 편집 6회에 요청 0건). 접힘 여부와 무관하게 지나는 emitSummary 로 옮김 —
  거기서 이미 고른 결과를 쓰므로 계산 비용 0.
- 응답이 오면 곡선을 다시 그리고, 그리기가 다시 schedule 을 불러 요청이 끝없이
  되돌아왔음. 입력 표식(측점 수 + 마지막·중간 누계)을 두어 같은 값이면 안 보냄.

자체검증(공용 브라우저, 패널 접은 채 계획고 6회 편집) — 요청 1건, 그 뒤 4초 동안
추가 0건. 고치기 전에는 편집 중 0건 + 펼친 뒤 1건 + 되먹임 1건이었음.
시험 400 통과·17 건너뜀, typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:13:05 +09:00
eomsangdon c25fc26f33 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 22:54:54 +09:00
eomsangdonandClaude Opus 5 4df5d0a7ea perf(B06): 카드 기하를 두 번 계산하던 것을 한 번으로
행 높이를 재는 1차와 실제 그리는 2차가 같은 측점의 crossPlotMetrics 를 각각 계산했음.
측점 67곳 기준 3.2ms/장이라 진입에서 draw 자기 시간 213ms 의 대부분이었음
(보조 창 CPU 프로파일).

- crossPlotMetrics 를 crossPlotBase(강제 높이 전) + crossPlotFromBase(높이만 얹기)로
  가름. 기존 시그니처는 둘을 잇는 감싸개로 유지.
- Section_View 1차에서 base 를 만들어 2차 카드에 그대로 넘김.
- 1차 반폭도 카드가 실제로 쓰는 값(effectiveCardHalfWidth, 자동 줌아웃 포함)으로 맞춤 —
  전에는 1차가 원래 반폭으로 재 카드·행 높이 기준이 어긋났음.
- 쓰이지 않게 된 crossCardNaturalHeight 와 그 재수출 삭제.

자체검증(공용 브라우저) — 카드 67장·SVG 67개 그대로, 같은 행 카드 높이 통일 유지
(rowsUniform true), 높이 범위 292~1,119px. 시험 400 통과·17 건너뜀, typecheck 통과.
효과 수치는 보조 창의 격리된 프로젝트에서 3회 중앙값으로 재기로 함(내 창은 편차가 큼).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:54:12 +09:00
eomsangdon 7060630f15 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 22:48:14 +09:00
eomsangdonandClaude Opus 5 c0bfcba34b feat(B05,B06): 유토 배분을 서버로 옮기고 브라우저는 미리 받아 쓰게 함
배분·운반거리 산식(_balance 523 + _settle 235 = 758줄)을 브라우저 번들에서 빼기 위한
배선. 계산은 여전히 한 벌 — 서버가 같은 TS 를 Node 로 돈다(CLAUDE.md 5장).

- POST /projects/{id}/sections/{route_id}/haul-plan 신설
  (B06_Section_Router_HaulPlan.py). 브라우저가 낸 누가토량 결과를 받아 배분만 돌려줌.
  화면이 쓰는 꼴 그대로 내보내 그리기 코드를 안 건드림. 표시 전용 — 정본은 저장 때 따로.
- B05_Profile_Api_HaulPlan.ts 신설 — 편집이 멈추면(400ms) 조용히 받아 두는 선반입기.
  늦게 온 응답은 버림(최신 요청만 채택). 못 받아도 곡선 자체는 그대로 보임.
- common_util_mass_haul.massHaulPayload 가 배분을 값으로 물던 것을 끊음(extra 인자).
  이 커플링 때문에 화면에서 안 불러도 번들에 남았음.
- 저장 경로는 곡선도 배분도 안 만듦 — 서버가 정본을 내므로 balloon 위치만 보냄.
- 죽은 파일 B06_Section_UI_Section_View_MassHaul.ts(236줄) 삭제.

자체검증(공용 브라우저, 용화 route 149) — 엔드포인트 200 · 196ms.
서버 배분과 브라우저 computeHaulPlan 결과가 JSON 문자열까지 동일(blocks 1 · steps 2 ·
spoil/borrow/hauled/transferred/fill_total 전부 일치). 시험 400 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:47:22 +09:00
eomsangdon f02eac5837 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 22:33:02 +09:00
eomsangdonandClaude Opus 5 1abb456ef9 perf(B06): 카드 넘침 정리를 한 프레임에 몰아 강제 레이아웃 67회를 1회로
카드마다 requestAnimationFrame(reflow) 을 걸어 한 프레임에 67번 돌았고, 매번
쓰기 뒤 읽기라 강제 레이아웃이 67회 났음. 한 번의 강제 레이아웃이 그때까지 들어간
카드 전부를 다시 재므로 뒤로 갈수록 비쌌음(보조 창 CPU 프로파일: 자기 시간 1위).

- reflow 를 reset(쓰기) / measure(읽기) / apply(쓰기) 세 토막으로 가름.
- 모듈 단위 scheduleReflow 가 카드를 모아 한 프레임에서 전부 reset -> 전부 measure ->
  전부 apply 순으로 돌림. 강제 레이아웃 프레임당 1회.
- ResizeObserver 는 창 크기 변경 때 그 카드만 큐에 다시 넣음(첫 호출 건너뛰기 유지).

자체검증(공용 브라우저) — B06 진입 2,330~2,864ms -> 1,994/2,088ms,
긴 작업 합 1,853 -> 1,604/1,728ms. 동작 확인: 카드를 200px 로 좁히면 버튼 3개가
... 패널로 옮겨지고 되돌리면 복귀(정상 폭에서는 scrollWidth = clientWidth = 937 로
넘침 없음). typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:32:17 +09:00
eomsangdon 81eb82a710 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 22:28:36 +09:00
eomsangdonandClaude Opus 5 5967d64b63 perf(B05): 측구 방향 역반영을 스레드 한 번 + 쓰기 한 문장으로 묶음
route/confirm 이 측점마다 종단 정본을 다시 열고(13.8ms) 행마다 DB 에 썼음(24.5ms).
건당 38.3ms 라 30건이면 1.15초, 67건이면 2.57초로 측점 수에 선형으로 늘었음
(보조 창 서버 내부 측정).

- 다시 계산할 측점을 먼저 고른 뒤 asyncio.to_thread 를 **한 번만** 돌림. 그 안에서
  종단 정본과 포장 제안표·세월교 하강표를 한 번 읽어 측점마다 돌려 씀.
- read_cross_design_inputs 에 preloaded 인자 추가(종단 경로·내용·포장 제안표).
  경로 검증은 resolve_longitudinal_path 로 떼어 재사용.
- 쓰기는 merge_cross_section_designs 한 문장.

자체검증(공용 브라우저 [저장] 3회) — route/confirm 470~834ms -> 324/351/356ms.
버튼 전체 대기 2,624~3,415ms -> 1,826/2,766/2,979ms.
(오늘 누적: 4,137ms -> 1,826~2,979ms) 시험 400 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:27:41 +09:00
eomsangdon 36a135f436 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 22:22:44 +09:00
eomsangdonandClaude Opus 5 0b30f5b035 perf(B05,B06): 지형 표고를 레이캐스팅 대신 격자 색인으로 · 카드 버튼줄 레이아웃 스래싱 제거
B05 진입이 간헐적으로 13~14초 멎던 원인을 CDP 프로파일로 잡음. 13,537ms 중
getVertexPosition 3,267 + intersectTriangle 3,178 + checkGeometryIntersection 3,012
+ _computeIntersections 2,198 = 11.6초가 three.js 레이캐스팅이었음.

- terrainElevation 이 점마다 Raycaster 로 지형 높이를 찾고 있었음(한 번 쏠 때마다
  삼각형 전수 훑기). 마커·측점선이 점마다 부르는 자리라 곱해짐. 이미 있던
  TerrainHeightIndex(격자 색인)로 돌리고, 색인도 지형과 한 벌로 보관(빌드 O(삼각형)).
  같은 교훈을 2026-08-23 비탈 투영에서 겪어 색인을 만들어 뒀는데 이 함수만 옛 길이었음.
- B06 카드 버튼줄 reflow 의 레이아웃 스래싱 제거 — 한 칸 옮길 때마다 scrollWidth 를
  다시 읽어 매번 강제 레이아웃을 냈음(카드 67장마다). 읽기/쓰기를 갈라 폭을 한 번만
  재고 옮길 개수를 계산. ResizeObserver 첫 호출도 건너뜀(rAF 와 겹쳐 두 번 돌았음).
- get_section_context·get_section_detail 의 순차 읽기를 asyncio.gather 로 묶음
  (원격 DB 왕복 약 12ms/질의).

자체검증(공용 브라우저 4왕복) — B05 진입 14,708/1,060/14,841 -> 1,269/806/726/746ms.
급등 사라짐. B06 진입 2,884 -> 2,330~2,864ms(긴 작업 합 2,313 -> 1,853).
시험 400 통과·17 건너뜀, typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:22:08 +09:00
eomsangdon 2476af0f4a Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-06 22:01:12 +09:00
eomsangdon 17ac08933f Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 22:01:09 +09:00
eomsangdonandClaude Opus 5 d650d705d1 perf(B06): 측점 설계 저장을 한 문장으로 묶어 원격 DB 왕복 제거
측점마다 SELECT+UPDATE 두 왕복을 냈고 DB 가 원격이라 왕복 하나가 약 12ms.
22행이면 670ms 이고 측점 수에 선형으로 늘었음(보조 창 서버 내부 측정).

- B06_Section_Repository_Bulk.merge_cross_section_designs 신설 — 노선 측점을 한 번에
  읽고, 파이썬에서 chainage 를 맞춰 JSON 을 합친 뒤
  `UPDATE ... SET data = CASE id ...` 한 문장으로 되돌려 씀(없는 행은 다중 INSERT).
  행 수와 무관하게 왕복 두 번. Repository 가 685줄이라 파일을 나눔(700줄 한계).
- 부르는 자리 셋을 묶음으로 바꿈 — _apply_section_edits 의 기본설계·측점 patch 두 루프,
  _recompute 의 보정·면적 두 루프.

자체검증(공용 브라우저 [저장] 3회) — sections/save 3,593ms -> 2,205 / 1,870 / 2,547ms.
버튼 전체 대기 4,137ms -> 2,624~3,415ms. 진행 표시는 2~3ms 만에 뜸.
시험 tmp/tests/test_b06_bulk_designs.py 5건(같은 JSON 결과·왕복 두 번·1cm 허용오차·
행 없을 때 INSERT·빈 목록은 왕복 0). 전체 400 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:00:28 +09:00
eomsangdonandClaude Opus 5 48e03e9d8a perf(B04): 유역 준비의 순차 질의 3건을 함께 보냄 + 헬퍼 한 곳으로
DB 가 원격이라 질의 하나가 곧 왕복 12ms 임. 서로 기다릴 이유가 없는 읽기를
한 커넥션에서 순차로 내면 그 왕복이 그대로 더해짐.

- config_db.run_with_connection: 저장소 함수를 자기 커넥션으로 돌려 gather 로
  묶을 수 있게 하는 공용 헬퍼. 정의를 한 곳에 둠(drainage_context 의 _query 는
  이 함수를 가리키는 이름으로 정리).
  머리에 경고 적음 — 순서가 필요한 쓰기는 이걸로 묶으면 트랜잭션이 깨짐.
- B04_PreProcess_Router_Watershed._prepare: 저장경로·좌표계·지표면확정값
  세 건을 gather 로. 약 24ms 절약.

common_util_auth_repository.py:318(decide_join_request)은 FOR UPDATE + 순서 있는
UPDATE 라 묶지 않음 — 읽기 블록만 대상.

자체검증 — 관 드래그 왕복 305 -> 259ms(최소 193), 관 11 · 유역 11 로 값 동일.
전체 484 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:57:10 +09:00
eomsangdonandClaude Opus 5 674777626e perf(auth): 세션 확인의 활동시각 쓰기를 1분에 한 번으로 — 34.4→13.6ms
인증이 걸린 모든 요청이 지나는 자리인데 요청마다 UPDATE + commit 이 나갔음.
DB 가 원격이라 그 둘이 곧 왕복 20ms 임.

실측 — verify_session 34.4ms = SELECT 10.8 + UPDATE 10.4 + commit 9.5.
쓰기를 1분 간격으로 낮춘 뒤 13.6ms(최소 9.4).

last_activity_at 은 유휴 판정에만 쓰는 값이고 그 판정이 4시간 단위라
초 단위 정확도가 필요 없음. 간격은 SESSION_ACTIVITY_WRITE_INTERVAL_SECONDS
(기본 60, .env 로 조정).

화면 한 번 여는 데 API 가 약 180번 나가므로 그만큼 곱해짐.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:52:38 +09:00
eomsangdonandClaude Opus 5 44ff1dea4d perf(B05): 화면에 들어올 때마다 지표면을 다시 파싱하던 것 제거
B05 는 해시가 바뀔 때마다 renderB05Route 로 통째로 다시 조립되어 뷰어 상태가 비워짐.
그래서 8MB 지표면을 진입할 때마다 다시 읽고 다시 파싱했고, 그것이 진입을 잡는
단일 동기 블록 14.4초였음(공용 브라우저 3왕복 실측: 14,708 / 1,060 / 14,841ms).

- 파싱해 둔 지형을 모듈 단위 cachedTerrain 에 한 벌 보관 — 같은 모델이면 새 장면에
  그대로 붙임. 다른 모델을 부르면 옛것을 버려 GPU 버퍼가 안 쌓임.
- 장면에서 뗄 때 보관본은 disposeObject 하지 않음.
- window.__surfaceTiming 디버그 훅 추가 — 단계별 시간(fetch·parse·fit·contours).

자체검증(공용 브라우저 3왕복) — B05 진입 683 / 474 / 639ms (전 14,708 / 1,060 / 14,841).
surfaceTiming: reuse 0ms · fit+markers 35ms · contours 183ms · total 222ms.
B06 진입은 2.4~2.7초로 변화 없음. 시험 395 통과·17 건너뜀, typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:52:13 +09:00
eomsangdonandClaude Opus 5 9a71f7bbaa feat(security): 호출량 감시 — 상한 넘은 세션만 시스템 로그에 표시
계산 결과는 화면에 나가도 된다는 방침이라, 남는 위험은 입력을 바꿔가며 출력을
긁어 모으는 것임. 사람이 화면을 쓰는 속도에는 한계가 있음 — 화면 한 번 여는 데
API 가 약 180회 나가므로 한 시간에 2만 회를 넘으면 사람이 아니라고 봄.

- 막지 않음. 고객 화면을 끊을 위험이 있고 어디서 끊을지는 실제 사용 기록을 본 뒤
  정할 일임. 지금은 시스템 로그(보관 1년)에 RATE_ANOMALY 한 줄만 남김.
- 세는 값은 메모리(세션·시간대별)라 요청당 DB 비용 0. 상한을 넘은 그 순간에만
  세션 주인을 한 번 조회해 기록하고, 같은 시간대에는 다시 안 남김.
- 지난 시간대 기록은 버려 사전이 무한정 자라지 않게 함.
- 상한은 API_CALL_HOURLY_LIMIT(기본 20000, .env 로 조정).

빌드 껍데기는 이미 단단해 손대지 않음 — 실측 확인: 배포 산출물에 소스맵 0개,
식별자 뭉개짐(import{$ as e,C as t,...}), computeCrossDesign 등 원본 함수명
검색 결과 0건. 난독화 도구 추가는 얻는 것 대비 비용이 큼.

시험 tmp/tests/test_call_volume_watch.py 5건. 전체 484 통과.
실서버 확인 — 정상 사용 흐름에서 감시 로그 0건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:45:12 +09:00
eomsangdon d0961ae71c Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-06 21:25:52 +09:00
eomsangdonandClaude Opus 5 d0ba8ed812 fix(main): 응답 압축 강도를 1로 — 기본 9가 요청을 9.4초로 늘림
등고선 도엽 36.8MB 를 매 요청 level 9 로 눌러 9.4초가 걸렸음(실측).
level 1 은 줄어드는 양이 거의 같고(66MB 기준 24.0 vs 23.9MB) 시간만 1/3.

실측(8001, IPv4) — 등고선 도엽 739ms · 전송 11.3MB(전 65.9MB 무압축),
종횡단 상세 485ms · 전송 0.33MB(전 1.56MB), 배수유역 전송 22KB(전 151KB).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:24:21 +09:00
eomsangdonandClaude Opus 5 3cd717d8f6 feat(B06): [저장]·[확정] 정본을 서버가 다시 계산하도록 되돌림
2026-09-06 저녁 사용자 확정. 이유는 속도가 아니라 보안 — 저장 경로가 브라우저 계산이면
유토 배분·운반거리 코드(_balance 523 + _settle 235 = 758줄)가 번들에 남아야 해서
화면에서 안 그려도 뺄 수 없음. 서버가 정본을 내면 그 몫이 번들에서 빠짐.
대가는 저장 대기 850ms -> 약 980ms(Node 131ms) — 기다리는 조작이라 허용.

- 저장·확정이 편집을 커밋한 **뒤** recompute_server_side 를 부름(전에는 편집 앞에서
  Node 없이 보정만). 바뀐 벽·측점이 면적·유토곡선에 실림.
- run_node 갈래 제거 — 이제 부르는 자리가 둘 다 Node 를 돎(체인·저장).
  쓰이지 않게 된 enforce_stored_designs 래퍼 삭제.
- 코리도 열쇠 질의: hash 를 isinstance 로 좁힘. 파이썬에서 곧바로 부르면 Query 기본값
  객체가 들어오는데 truthy 라 열쇠 없는 호출이 통째로 stale 로 떨어졌음(보조 창 지적).

시험 395 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:20:55 +09:00
eomsangdonandClaude Opus 5 da2130c988 perf(B04): 응답 압축 + 좌표 자릿수 정리로 내려보낼 양 축소
DB·파일에는 뜻 없는 자릿수가 붙어 있고(1.4000000000000001 꼴) 서버는 응답을
전혀 압축하지 않고 있었음. B05 한 번 진입에 118MB 수신(2026-09-06 실측).

- main.py: 압축 미들웨어(1KB 이상). 3D 예상형상(/corridor)은 예외 —
  숫자 배열이라 절반만 줄면서 압축에 493ms 듦(18.6->9.4MB).
- common_util_json: round_floats(value, digits) + LONLAT_DIGITS 7(1.1cm) ·
  METRE_DIGITS 6. 저장 파일은 그대로 두고 내려보낼 때만 줄임.
  브라우저가 다시 계산에 넣는 값(종횡단 지반선)은 줄이면 안 된다고 머리에 경고 적음.
- 도엽 GeoJSON: 자릿수만 줄인 표시용 사본(.display.geojson)을 한 번 만들어 서빙.
  원본이 새로 깔리면 자동 재생성, 실패하면 원본 그대로.
  등고선 도엽 65.9->36.8MB, 압축까지 11.3MB.
- 배수유역 응답: 위경도 7자리. 151->100KB, 압축 22KB.

ruff 통과, 테스트 390 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:13:05 +09:00
eomsangdonandClaude Opus 5 c97e949792 fix(B06): 서버가 못 뜨던 import 수정 — 확정 라우터의 옛 재수출 경로
`B06_Section_Router_Confirm` 이 `_compute_default_designs` 를 `B06_Section_Router`
에서 들여왔는데, 커밋 1d4568b9 가 그 재수출을 지우면서 서버가 아예 안 떴음.
원본 모듈 `B06_Section_Router_Design.compute_default_designs` 에서 곧바로 들여옴.

시험 390건이 이 경로를 안 타서 못 잡았으므로 tmp/tests/test_app_imports.py 추가 —
`main` 을 들여와 라우터 27개가 전부 import 되는지 본다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:12:41 +09:00
eomsangdonandClaude Opus 5 59df765e30 perf(B05): 3D 진입에서 저장본을 받아놓고 버리는 일 제거
B05 에 들어가는 것만으로 코리도 저장본 18.6MB 를 통째로 받고, 열쇠(해시)에 종횡단
내용이 섞여 있어 계획고를 한 번이라도 만졌으면 받아본 뒤 버리고 브라우저가 다시
만들었음(실측 화면 전환 16.7초). 3D 는 이미 [3D 업데이트] 수동인데 진입 경로만 자동.

- 서버: GET corridor 에 `?hash=` 를 받아 저장본 머리 4KB 에서 열쇠만 떼어 맞춰 봄.
  어긋나면 파일 대신 `{status:stale}` 만 냄. 맞으면 종전대로 파일 — 왕복은 한 번.
- 화면: 진입은 `loadCorridorIfFresh` 로 **저장본이 맞을 때만** 3D 에 올림.
  낡았으면 받지도 만들지도 않고 [3D 업데이트] 대기 표시만 켬.
- `ensureCorridor` 에 `rebuild` 인자 추가 — [3D 업데이트] 버튼 경로는 종전대로 재생성.

시험 tmp/tests/test_b05_corridor_hash_probe.py 3건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:07:11 +09:00
eomsangdon 1bb367058b auto: 2026-09-06 20:58 (ESD_LAPTOP) 2026-09-06 20:58:04 +09:00
eomsangdon d5617a8b58 @
perf(B04): 배수유역 세부 계산의 헛일 제거 — 282→154ms

DB 가 원격(dsm.chemifactory.com)이라 질의 하나가 곧 왕복 12ms 임을 실측.
순차 6건 130ms 중 대부분이 왕복 대기였음.

- context: 서로 기다릴 이유가 없는 질의를 두 묶음으로 asyncio.gather
  (묶음마다 자기 커넥션 — 풀 최대 20 이라 여유). 계획노선 읽기·지표면
  샘플러 열기도 같이 보냄.
- 격자 산출물(npz 3.1MB): 관을 옮길 때마다 다시 읽던 것을 파일 자국
  (수정시각·크기) 열쇠로 재사용. 분석이 다시 돌면 자국이 바뀌어 저절로 새로 읽음.

자체검증 — 용화 프로젝트(관 11·유역 11) 5회 중앙 282→154ms.
같은 입력에 응답 전체가 한 글자도 안 달라짐(JSON 정렬 비교).
전체 테스트 390 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@
2026-09-06 20:57:51 +09:00
eomsangdonandClaude Opus 5 165b281fe1 fix(B05,B06): 배수관 편집을 세션 초안에 쌓아 B06 저장에도 실리게 함
관 추가·이동·삭제가 B05 패널 메모리에만 있어, B06 으로 넘어가 [저장]·[확정]하면 그
편집이 사라졌음(대응표 조사에서 드러남).

- 등록표에 `pipes` 초안(프로젝트 범위)을 되살림
- 배치가 바뀌는 순간 목록을 초안에 담고, 내보내기는 `flushPendingPipes` 한 창구로 모음
- B05 [임시저장]·B06 [저장]·[확정] 모두 이 창구를 지남 (자동저장 없음)

검증: 초안으로 관 11 → 10개 저장·초안 비움, 되돌려 11개 복구. 테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 20:25:12 +09:00
eomsangdonandClaude Opus 5 81a86fcc0d fix(B06): 저장 뒤 상세 캐시를 버려 새 구조물 벽이 붙게 함
구조물 초안이 저장으로 정본이 되면, 화면이 들고 있던 상세는 초안 기준으로 벽을 얹은
사본이라 새로 저장된 구조물의 벽이 다음 조회까지 안 붙었음(8+0.0 흙막이 실측).

검증: 캐시를 비우면 벽 5 → 7개로 붙음. 테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 20:20:08 +09:00
eomsangdonandClaude Opus 5 d529ebd170 feat(B06): 지형에 막힌 좌우 이동을 벽 툴팁으로 알림
추가 기슭막이를 옆으로 옮겨도 안 움직이는 측점이 있어(실측 1.0m 요청에 0.00~0.18m),
막는 규칙(원지반 매몰 금지)이 되돌린 양을 벽에 남기고 툴팁에 적음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 20:13:47 +09:00
eomsangdon 8ee858e8fa docs(knowledge): fix forest-road manual layout 2026-09-06 20:10:51 +09:00
eomsangdon 8086c57e18 auto: 2026-09-06 20:05 (ESD_LAPTOP) 2026-09-06 20:05:20 +09:00
eomsangdon 4b26a5e7d2 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-06 20:04:33 +09:00
eomsangdon 507a7cfc43 auto: 2026-09-06 20:03 (ESD_LAPTOP) 2026-09-06 20:03:36 +09:00
eomsangdonandClaude Opus 5 89820848ec fix(B06): 추가 기슭막이 바닥을 원지반에 앉힘
연계(추가) 기슭막이가 성토사면과 안 맞는다는 지적(3-5)을 수치로 재니, 어긋난 곳은
접점이 아니라 바닥이었음 — 상단은 1:1.2 성토선에 붙는데 키는 요청값 그대로라
지형이 모자라면 바닥이 떴음(실측 0.12~0.87m).

자리는 그대로 두고 키를 지반까지 맞춤 — 지반이 낮으면 형태 높이 한계까지 키우고,
높으면 최소 높이까지 줄임. 조정창 되받기 값도 조정된 높이로 나감.

검증: 264.06 0.394 → 0.000m(키 1.00 → 1.39), 821.62 0.427 → 0.000, 914.93 0.122 → 0.000.
731.31 은 0.873 → 0.373 으로 줄되 남음(돌쌓기(메) 높이 한계 2.0m — 경고 유지가 맞음).
테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 20:03:28 +09:00
eomsangdonandClaude Opus 5 818042e45c feat(B05,B06): 최종 누가토량 배지 + B06 유토곡선 그래프 제거
사용자 지시(2026-09-06): 유토곡선은 자리를 많이 먹는데 정작 늘 봐야 하는 값은 마지막
지점 누가토량 하나임. 기준은 횡단으로 고정(종단 기준 곡선은 뒤에 삭제 예정).

- 총괄값 계열을 범례 선택과 무관하게 **횡단 기준**으로 뽑음
- 공용 배지 `common_util_mass_haul_badge.ts` — 종단 그래프 좌측 상단 오버레이,
  세부 내역은 툴팁
- B05: 상단줄의 「누가토량」 항목을 배지로 옮김(유토곡선 패널은 그대로 유지)
- B06: 유토곡선 그래프·범례·요약줄 삭제, 그 높이를 종단면도가 전부 사용

검증: B06 배지 −5,595.5㎥·범례 없음·종단 228px, B05 배지 −6,842.6㎥·상단줄에서 제거.
테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 19:44:59 +09:00
eomsangdonandClaude Opus 5 d057e64e9f style(B05): 구조물 알약을 한 단 낮춰 종단 그래프 자리 확보
사용자 지시(2026-09-06): 구조물 표시가 너무 커서 높이를 많이 차지함.

- 알약 높이 24 → 18px, 혼자 있을 때 31 → 22px, 글자 11 → 10px
- 레인 전체 54 → 42px — 그만큼 종단 그래프가 세로를 더 씀

검증: 실화면 레인 42px·알약 22px, 이름표(찰쌓기·배수관·옹벽·세월교) 모두 읽힘.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 19:28:44 +09:00
eomsangdonandClaude Opus 5 1136fff0ea fix(B06): 구조물 초안·면적을 상세 저장소 한 곳에서 얹음
페이지마다 따로 얹으니 새로고침 뒤 세션 스냅샷이 저장분 기준으로 되돌아갔음
(초안 벽 3건이 화면에서 사라짐). 캐시·세션·새 요청 어느 길로 들어오든 지나는
공유 저장소에서 ① 저장 안 한 구조물 벽을 얹고 ② 구조물 면적을 다시 계산하게 모음.

검증: 새로고침 뒤에도 벽 5건(저장 2 + 초안 3)이 붙고 3+0.0 성토 8.06 → 5.73㎡ 유지.
테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 19:18:58 +09:00
eomsangdonandClaude Opus 5 155641a782 fix(B06): 구조물 추가 시 유토곡선도 같이 갱신
유토곡선은 카드보다 먼저 계산돼, 구조물을 넣으면 카드 면적만 바뀌고 최종 누가토량은
옛 값으로 남았음. 구조물이 바뀌면 면적을 먼저 다시 얹고 그리게 순서를 바로잡음.

검증: 3+0.0 성토 8.06 → 5.73㎡, 최종 누가토량 553.4 → 546.4㎥ (저장 전, 즉시).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 19:04:50 +09:00
eomsangdon c5ad7b7303 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 19:02:49 +09:00
eomsangdonandClaude Opus 5 1d4568b92f feat(B06): 구조물 목록의 C군 벽을 횡단 면적에 반영
좌측 「구조물 배치」로 넣은 옹벽·돌쌓기 같은 벽이 지금까지 측점만 심고 기하가 없어
절·성토 면적이 그대로였음(2026-09-06 사용자 확정: 반영해야 함).

- 독립 기슭막이가 쓰던 `section.revetment` 제원 자리에 그대로 얹음 — 기하·설계선
  트림·폐회로 면적·3D 가 손대지 않고 따라옴.
- 서버 `B06_Section_Engine_Structures_Wall.py`(저장분) ↔ 브라우저
  `common_util_structure_walls.ts`(미저장 초안) 짝 + 거울 테스트.
- 설치 측 칸이 없는 C군은 성토가 나는 쪽으로 자동 배치, 양측 절토면 세우지 않음.
- 구조물 목록이 바뀌면 B06 이 초안을 얹어 다시 그림.

검증: 40m 돌쌓기(찰) 추가 시 그 측점 성토 5.81 → 5.02㎡. 테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:59:55 +09:00
eomsangdonandClaude Opus 5 4066504ba9 feat(B01): 시스템 로그에 대상·접속 정보 기록 + 1년 보관 정리 (2026-09-06 사용자 확정)
- 기록을 `common_util_audit.record_audit()` 한 곳으로 모음 — 여섯 자리에 흩어져 있던
  raw INSERT 제거
- 대상 식별자 칸 신설(`017_audit_log_detail.sql`, 적용 완료) — 기존 `resource_id` 는 INT 라
  프로젝트 UUID 를 못 담아 늘 NULL 이었음. 프로젝트 생성·수정·삭제·회사 생성이 대상을 남김
- 접속 주소·브라우저 기록 — 라우터가 요청을 넘겨 주고, 프록시 뒤에서는 X-Forwarded-For 우선
- 보관 기간 `AUDIT_LOG_RETENTION_DAYS` 기본 365일, 임시 보관함 정리 루프에 얹어 함께 정리
- 화면: 시스템 로그 표에 대상·접속 주소 열 추가(대상은 UUID 앞 8자만)

자체검증 — 프로젝트 생성·하드삭제를 실화면에서 돌려 두 줄 모두
`프로젝트 e1040640… · 127.0.0.1 · 2026-09-06 09:54/09:55` 로 남는 것 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:57:22 +09:00
eomsangdonandClaude Opus 5 d50956381d feat(B01): 대시보드 표·기본정보 정비 (2026-09-06 사용자 지시)
- 프로젝트 표의 진행도(%) 열 삭제 — 워크플로 배지가 같은 것을 보여 줌. 상태 문자열로
  따로 세던 서버 계산도 제거(배지는 `project_workflow_stages` 표가 근거라 둘이 어긋났음)
- 시스템 로그 표 = 이메일 · 동작 · 대상 · 일시. 「관리」 문구를 돌려 쓰던 것을 가르고,
  대상(resource_type·id)을 새로 보이며, 일시는 날짜/시각 두 줄(아랫줄 작은 글씨라 행 높이 불변)
- 표 안 관리 버튼을 한 줄로 — 작은 버튼 + 줄바꿈 금지, 폭이 모자라면 표가 가로 스크롤
  (프로젝트 행 높이 105px → 61px, 사용자 행 55px)
- 기본정보는 로그인 본인 화면 — 이메일 라벨을 「팀원 이메일」에서 「이메일」로 바꾸고
  본인 서명 칸을 사용자 수정 모달과 같은 부품으로 추가
- 제목·여백을 공용 템플릿(`createGeneralLayout`)으로 통일, 역할 배지는 제목 줄 오른쪽

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:41:02 +09:00
eomsangdonandClaude Opus 5 cdb9799e64 fix(B05): 구조물 저장이 판번호 때문에 통째로 사라지던 것 수정
화면 진입 때 받은 판번호로 저장해, 그 사이 정본이 한 번이라도 바뀌면(다른 창
저장·노선 재계산) 409 로 거절되고, 실패 처리가 세션 초안까지 지워 사용자가 넣은
구조물이 사라졌음.

- 판번호를 **쓰기 직전에** 서버에서 다시 받아 씀.
- 저장이 실패해도 세션 초안·화면 목록을 지우지 않음(다시 [저장]하면 됨).

실화면 검증: 고치기 전 구조물 추가 후 [저장] → 정본 0건. 고친 뒤 → 정본 1건, 2건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:38:04 +09:00
eomsangdon 92336db3c2 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 18:27:34 +09:00
eomsangdon 8dab657930 auto: 2026-09-06 18:14 (ESD_LAPTOP) 2026-09-06 18:14:35 +09:00
eomsangdonandClaude Opus 5 be78af0460 feat(B05): 계획노선 편집 모달 — 예상노선 위에서 노선 고치기
좌측 「계획노선」 섹션의 [계획노선 편집] 로 큰 모달을 엶(PLAN 0-2).

- 등고선 도엽 위에 예상노선(점선)·계획노선(실선)을 함께 그림. 지도 그리기는
  배수유역도와 같은 도구(`B04_PreProcess_UI_MapRender`) 재사용.
- 노드 끌어 옮기기 · 선 두 번 클릭으로 노드 끼우기 · 오른쪽 클릭으로 지우기,
  배경 끌기로 화면 이동, 휠로 확대.
- 편집 중에는 계산이 나가지 않음. [확인]에서만 서버가 배수유역부터 재계산하며
  그동안 화면을 덮는 안내를 띄움. 끝나면 세션 초안·조회 캐시를 비우고 페이지를
  다시 세움.
- [예상노선으로] 는 수정본을 지우고 같은 재계산(노선 초기화).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:14:29 +09:00
eomsangdonandClaude Opus 5 2df959830b fix(B02): 등록 화면 폭을 종전 720px 로 되돌림
공용 템플릿으로 바꾸면서 페이지 기본 폭(--page-max-width)을 따라가 넓어졌음 —
템플릿은 그대로 쓰되 이 화면의 안쪽 폭만 720px 로 좁힘.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:01:12 +09:00
eomsangdon 9743416301 auto: 2026-09-06 18:00 (ESD_LAPTOP) 2026-09-06 18:00:40 +09:00
eomsangdonandClaude Opus 5 8e6a1e27eb feat(B05): 계획노선 두 벌 + 노선 갈아 끼우기·초기화 서버 경로
노선을 예상노선(원본)·계획노선(수정본) 두 벌로 나눔 (PLAN 0-7 사용자 확정).

- 예상노선 정본 `B05_Profile/route/expected_route.csv` 신설 — 자동 체인이 한 번
  씀. 초기값 스냅샷 안에도 같은 CSV 가 있으나 그 폴더는 재확정 체인이 지우므로
  스냅샷 밖에 한 벌 둠.
- 계획노선 수정본 `B05_Profile/route/planned_route.csv` — 설계 계통(`load_design_route`)이
  수정본 → 예상노선 → 스냅샷 순으로 읽음. 노선 초기화는 수정본을 지우는 것.
- `GET /route/plan` 두 노선 정점 반환(모달이 점선·실선으로 그림).
- `POST /route/replan` 고친 노선을 수정본에 쓰고(조밀화) 재확정 체인 재사용 —
  배수유역 다시 → 관 새로 → 계획선·종횡단 재생성 → 옛 측점 설계 누가거리 이월.
- `POST /route/replan/reset` 수정본 삭제 후 같은 재계산.
- 횡단배수 지점은 노선이 바뀌면 저장분을 버리고 새로 계산(사용자 확정).
  구조물은 프로젝트 정본이라 옛 측점값 그대로 남음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:00:34 +09:00
eomsangdonandClaude Opus 5 4b5a630f12 fix(B02·A00): 프로젝트명 연동 끊김·항목 순서·공용 템플릿·헤더 버튼 간격
- 프로젝트명 조합기를 글자 단위 소유 추적으로 다시 만듦(`B02_ProjRegister_UI_Name.ts`) —
  자동 글자 사이에 글을 끼워 넣어도 사업연도·사업지역·임도종류 연동이 끊기지 않음
  (실측: 「울진군 금강송면」의 「금강송면」 앞 삽입 후에도 지역·종류·연도 변경이 반영)
- 비고 칸을 맨 마지막으로 이동
- 제목·여백·버튼 줄을 공용 템플릿(`createGeneralLayout` · `ui-general-block__actions`)으로 전환,
  화면 전용 제목 스타일 제거
- 헤더의 사용자명·로그아웃(로그인·회원가입) 버튼이 붙어 보이던 것 간격 부여

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:59:22 +09:00
eomsangdon 9f35ecd2d8 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-06 17:49:45 +09:00
eomsangdonandClaude Opus 5 489a53f25f feat(B02): 프로젝트 등록에서 회사 로고 칸 제거
프로젝트가 이미 회사에 매여 있어 따로 고를 이유가 없음 — 도면 표제란은
`COALESCE(프로젝트 로고, 회사 로고)` 로 조회하므로 회사 로고가 그대로 쓰임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:49:37 +09:00
eomsangdonandClaude Opus 5 861068cbf4 feat(B02): 프로젝트명을 고칠 수 있는 입력 칸으로 전환·비고 위치 이동
- 「저장될 이름」 미리보기 줄을 없애고 프로젝트명 입력 칸 하나로 통합 — 사업연도·사업지역·
  임도종류로 자동 채우되 사용자가 사이나 끝에 글을 넣거나 자동 부분을 고칠 수 있음
- 손댄 뒤 위 세 항목을 바꾸면 해당 조각만 갈아 끼워 사용자가 넣은 글은 보존
- 비고 칸을 노선 시작 누가거리 앞으로 이동

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:46:09 +09:00
eomsangdonandClaude Opus 5 72075563e3 feat(B03·B04): 지형 라이다 여러 장 입력·병합 전처리
- 지형 파일 개수 제한 해제(정확히 1개 → 1장 이상), 한 카드에 여러 장 담기
  (화면 표시 「용화_서편.las 외 1장」, 업로드는 한 장씩 차례로 전송)
- 구조화 엔진이 여러 파일을 합친 범위로 한 벌 생성 — WF1 자동 전처리·B04 재분석 모두
  프로젝트 지형 파일 전부를 대상으로 실행
- 점이 1억 개를 넘으면 씨닝 — 지면 분류점은 전부 남기고 나머지만 0.5m 칸 최저점으로 축소
  (용화 실측: 4,900만점 → 249만점, 지면점 1,140,716개 그대로, 1m 지면격자 표고차 0.0000m)
- 머리글만 읽어 5km 넘게 떨어진 파일은 업로드 거부 (임도는 길어도 2~3km)
- 화면 조립부 700줄 준수를 위해 terrainCoverage 를 판정 모듈로 이동

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:45:59 +09:00
eomsangdonandClaude Opus 5 2d0895d975 fix(B03): LAS 없이 설계 업로드가 지형 PRJ·TFW 필수 검사에 막히던 것 해소
- 「LAS 없이 설계」면 지형 한 벌(포인트클라우드·지형 PRJ·TFW)을 필수에서 면제
- 업로드 요청 검사도 완료 검사와 같은 기준으로 노선 PRJ 와 지형 PRJ 를 갈라 셈
  (종전에는 확장자만 봐서 노선 PRJ 를 지형 PRJ 로 오인)
- 업로드 현황의 「필수 충족」은 stage 0 완료를 우선 근거로 판정 — B04 이동 버튼 복구

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:45:43 +09:00
eomsangdon a22886c4c9 auto: 2026-09-06 17:22 (ESD_LAPTOP) 2026-09-06 17:22:50 +09:00
eomsangdonandClaude Opus 5 402800435d feat(B05): 유토곡선 전체 맞춤에서 측점선 숨김·좌우 여백 축소
- 전체 맞춤(토량 분배) 보기는 측점 세로선을 그리지 않음 — 67개가 촘촘히 서서
  곡선을 덮기만 하고 자리도 못 읽음. 공용 차트에 `hideStations` 축 옵션 신설,
  분배를 끄면 예전대로 나옴.
- 같은 보기에서 좌우 여백 축소 — 종단과 맞물릴 필요가 없으므로 반 칸 들여쓰기
  (originOffset)를 빼고 우측 여백 12px. 곡선이 폭을 다 씀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:22:43 +09:00
eomsangdon d69d4eb6ec auto: 2026-09-06 17:07 (ESD_LAPTOP) 2026-09-06 17:07:07 +09:00
eomsangdonandClaude Opus 5 94266757cd feat(B05): 토량분배 시 유토곡선 전체 맞춤 + 상단 여백·테이블 높이 정리
- 토량분배를 켜면 유토곡선이 종단과의 X 맞춤·세로 창을 버리고 스크롤러 안쪽
  폭·전 구간 Y 로 그려 곡선 전체가 한 화면에 들어옴(평형선·운반 블록을 노선
  전체로 읽는 그림이라). 끄면 예전대로 종단과 맞물린 가로 스크롤로 복귀.
- 요약 막대가 상단줄로 올라간 뒤 남던 상단 여백 축소 — 범례 top 34px → 4px,
  차트 padTop 40 → 30(범례 높이 24 + 여유).
- 종단 테이블 오버레이 높이 상한 신설 — 6행 × 행 30px + 24px(=204px).
  12행에서 6행으로 줄인 뒤 같은 높이를 나눠 가지며 행이 부풀던 것을 막음.
  레이아웃 제어(리사이저·패널 비례 연동)는 그대로 두고 상한만 걸음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:07:01 +09:00
eomsangdonandClaude Opus 5 43f6325d30 knowledge(교본): 임도 교본 2~6장 수식·표 정리
산림과임업기술(임도) 2장 임도구조 · 3장 임도계획 · 4장 노선측량 ·
5장 설계 · 6장 시공에서 한 줄로 뭉개져 있던 공식을 LaTeX 수식 블록으로
분리하고 기호 설명을 목록화. 본문 값·출처 불변.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:55:41 +09:00
eomsangdonandClaude Opus 5 a1a5081f28 fix(B05): 종단 그래프를 끌 때 글자가 선택되던 것 막음
포인터 잡기를 끌기 시작 뒤로 미룬 뒤(측점 선택 복구), 그 전까지 브라우저 기본
글자 선택이 살아나 끌 때마다 라벨·수치가 파랗게 잡혔다.

끌기가 실제로 시작되는 순간 `user-select: none` 을 걸고 이미 잡힌 선택을 푼다.
끝나면 원래대로 되돌린다.

검증(공용 브라우저, 실제 마우스): 끄는 동안 선택된 글자 0자, 끝난 뒤 속성 원복,
측점 클릭 선택은 그대로 동작(60m).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:43:01 +09:00
eomsangdonandClaude Opus 5 7b0aced287 fix(B05): 종단 그래프에서 측점을 골라도 선택이 풀리던 것 수정
끌어서 이동이 pointerdown 즉시 `setPointerCapture` 를 걸어, 뒤따르는 click 의
대상이 측점 `<g>` 가 아니라 스크롤러가 됐다. 그래서 측점 클릭이 배경 클릭으로
처리돼 선택이 그 자리에서 풀렸다(사용자 보고: 측점을 골라도 하이라이트가 안 됨).

- 포인터 잡기를 **끌기 문턱을 넘긴 뒤로** 미룸. 단순 클릭은 캡처가 없어 측점에
  그대로 닿고, 실제로 끌기 시작한 뒤에는 잡아 두어 창 밖에서 손을 떼도 이어진다.
- 문턱 4px → 6px. 손 떨림에 쉽게 넘어가 측점을 고르기 어려웠다.

검증(공용 브라우저, 실제 마우스 입력): 측점 클릭 → 선택·하이라이트 정상(420m),
끌기 → 선택 유지하며 팬 이동(scrollLeft 1660 → 1780).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:37:38 +09:00
eomsangdonandClaude Opus 5 7086959e10 chore(git): B07 도구 무시목록 되돌림
같은 폴더를 B07 창이 `05a6b7e5 feat(B07): LibreDWG 변환기 동봉` 로 일부러
저장소에 넣어 두었다. 그 결정과 어긋나므로 무시 규칙을 뺀다.
(되받기 전에는 그 커밋이 여기 없어 추적되지 않은 파일로만 보였다.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:21:47 +09:00
eomsangdon 724adf25cd Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-06 16:20:55 +09:00
eomsangdonandClaude Opus 5 370ebb7cd2 chore(git): B07 DWG 변환 도구 폴더를 무시목록에 추가
LibreDWG 배포판(33MB 실행파일·DLL)이 추적되지 않은 채 남아 git sync 의 다른 창
브랜치 되받기가 계속 막혔다. 내려받아 쓰는 외부 도구라 저장소에 넣지 않는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:20:45 +09:00
eomsangdonandClaude Opus 5 6fe7004946 knowledge(교본): 산림과임업기술(임도) 그림 파일명 의미화
페이지 번호식 이름(`..._p101_1.png`)을 내용이 드러나는 이름으로 바꿨다.
예: `노선측량_그림5-2-27_중심선과영선의위치비교.png`

- 그림 120개 이름 변경(122개 신규 등록), 본문 md 12개의 링크를 새 이름으로.
- 추출 파이프라인(`_pipeline/pdf2md.py`)과 QC 리포트 갱신.

확인: 그림 127개 · md 이미지 링크 127개 · 깨진 링크 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:17:10 +09:00
eomsangdonandClaude Opus 5 1cf47f39b2 fix(B06): 임시저장 엔드포인트가 깨져 있던 것 수정
포장·세월교 보정 헬퍼를 저장 시점으로 옮기면서 `@router.post(.../save)`
데코레이터와 `save_sections` 사이에 끼워 넣어, 라우트가 헬퍼(None 반환)에
붙어 있었다. 저장이 500(ResponseValidationError)으로 떨어졌다.

헬퍼를 데코레이터 앞으로 옮겨 라우트를 되돌렸다.

검증(용화 신규 프로젝트, 노선 145): 저장 200, 카드에서 고른 지반유형이
DB `cross_sections.data.design.ground_type` 에 반영되고 면적도 브라우저
계산값으로 갱신됨.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:11:40 +09:00
eomsangdonandClaude Opus 5 d0d3f2a311 feat(B07): 자리표 마우스 크기조절·기본 도각 칸 정렬·도면명 미리보기
- 칸이 있는 글자와 그림에 모서리 그립 추가, 모서리를 끌어 칸 크기를 바꾸는 조작 신설 (다른 도면 요소에도 동일 적용)
- 기본 도각(00_template_A1)의 글자 24개에 표제란 칸을 계산해 부여 — 칸 기준 가로·세로 가운데 정렬
- 도각 편집 시 도면명·도면번호도 보던 도면 값으로 미리보기 제공
- 복제 시 미리보기 값 유지 (그립 편집 후 자리표가 토큰으로 되돌아가던 문제)
- CAD index.html 을 캐시하지 않도록 처리 — 빌드 후에도 옛 화면이 남던 문제 해소

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:52:36 +09:00
eomsangdonandClaude Opus 5 df2dedcbc4 perf: 화면 진입 중복 호출 정리 — 세션·워크플로 상태·구조물 이관
실측(B05 진입): auth/session 4회, workflow-state 2회, 구조물 이관 POST 가 진입할
때마다 나갔다.

- auth/session: 라우터 가드·상단 바·워크플로 확인이 각각 부르던 것을 5초 캐시로
  묶음. 로그인·로그아웃에서 즉시 버린다.
- workflow-state: 공용 창구 하나로 모으고 같은 방식으로 캐시. 상단 오버레이가
  따로 부르던 경로를 그 창구로 돌림. 단계 이동에서 캐시를 버린다.
- 구 구조물 측점 이관 POST: 탭 수명 동안 노선마다 한 번만.

실측 결과: B05 첫 진입 20 → 18건(핵심 API 10건), B06→B05 이동 7건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:28:20 +09:00
eomsangdonandClaude Opus 5 b0c2e09b16 feat(B07): 자리표 칸 크기 조절·그림 비율 유지·편집 중 실제 값 미리보기
- 텍스트 자리표에 칸 크기(boxWidth/boxHeight) 도입 — basePoint 를 칸 중심으로 두고 가로·세로 가운데 정렬, 선택 시 칸 테두리 표시, DXF 내보내기도 중앙 정렬로 반영
- 그림은 칸 안에서 비율을 유지하며 맞춤(letterbox) — 칸을 늘려도 로고·서명이 찌그러지지 않음
- 자리표 패널에 선택 항목 칸 크기(가로·세로 mm) 입력 추가
- 도각 편집 중 자리표에 실제 값 미리보기 — 서버가 표제란 값(공사명·회사·담당자·로고·서명)을 함께 내려주고 화면만 값으로 표시, 저장값은 {{키}} 토큰 유지
- title_block_fields 공개화 및 frame-template 응답에 fields 추가

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:17:39 +09:00
eomsangdonandClaude Opus 5 9da3bce1bf fix(B06): 횡단 카드 버튼이 서버에 바로 저장하던 것을 캐시로 바꿈
사용자 확정(2026-09-06): 조작은 캐시에 쌓이고 [저장]·[확정]에서만 영구저장소에
나간다. 카드 버튼(지반유형·단면유형·측구·포장·2단 비탈)은 누를 때마다
POST cross-design 이 나가 서버가 계산하고 바로 저장하고 있었다.

- 세션 초안 crossdesign 신설(B06_Section_Cross_Design_Session) — 선택값만 담는다.
- 계산은 브라우저 재계산 창구 하나로 돌린다(B05·B06 공용).
- 재계산 때 세션 선택이 정본보다 우선 — 새로고침 뒤에도 고른 값이 남는다.
- [저장]·[확정] patch 에 선택값 + 전 측점 면적을 실어 보낸다. 카드 버튼을 바꾸면
  구조물이 없는 측점 면적도 달라지므로 구조물 측점만 보내면 수량이 어긋난다.
- 구조물 목록 주석 정정(이미 초안 방식인데 옛 주석만 남아 있었다).

검증: tsc --noEmit 통과, pytest 389 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:14:44 +09:00
eomsangdonandClaude Opus 5 8e3c1f91fc perf(B05/B06): 진입 시 중복 서버 호출 두 건 제거
- 확정 지표면: 목록(surface/models)으로 id 를 찾고 3D 단계에서 surface/confirmed
  를 또 부르던 것을 확정 응답 한 번으로 합침. 모델 id·범위가 그 응답에 다 있다.
- 종횡단 설정값(sections/context): ④ 계산 결과로 보고 세션에 담음. B05·B06 을
  오갈 때마다 다시 묻지 않는다. 노선 캐시를 버리는 자리에서 함께 버린다.

검증: tsc --noEmit 통과, pytest 389 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:07:47 +09:00
eomsangdonandClaude Opus 5 a360c0caba feat(B05): 구조물 측점과 겹치는 규칙 측점 제거
구조물은 보조말뚝을 박아 그 자리가 정본 측점이 된다. 규칙 격자(20m)와 0.1m
안에서 만나면 세로선·라벨이 두 겹으로 겹쳐 읽히지 않았다.

- 공용 유틸 dropStationsNear 추가(B05_Profile_Util_Station).
- 종단 그래프와 3D 측점 띠가 같은 규칙을 쓴다 — 두 화면의 측점이 어긋나지 않는다.

검증: tsc --noEmit 통과, tmp/tests/test_b05_station_merge.mjs 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:04:40 +09:00
eomsangdonandClaude Opus 5 bdbefd9472 feat(B06): 곡선부 확폭에 앞뒤 10m 테이퍼 추가
별표2 는 확폭량 표만 주고 붙이는 방식이 없어, 곡선 시·종점에서 폭이 뚝 끊겼다.
곡선 앞뒤 CURVE_WIDENING_TAPER_M(10m) 구간에서 0 → W 로 잇는다.

- 측점 생성 때 확폭량을 한 번에 계산해 curve_widening_m 으로 저장. 테이퍼는
  이웃 측점을 봐야 하므로 측점 단위 계산으로는 낼 수 없다.
- 설계 엔진(파이썬·TS 짝)이 저장된 확폭량을 우선 쓰고, 없으면 반경 표값으로
  되돌아간다 — 옛 저장분도 그대로 돈다.

검증: pytest 389 passed(테이퍼 경계값 테스트 2건 추가), tsc --noEmit 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:02:58 +09:00
eomsangdonandClaude Opus 5 dd61754705 refactor(B06): 구조물 면적·유토곡선을 브라우저 계산으로 되돌림
사용자 확정(2026-09-06): 조작 중과 [저장]·[확정]의 계산은 브라우저 몫이고,
서버는 초기값을 만들 때만 같은 코드를 Node 로 돌린다.

- 면적 산출을 B06_Section_Structure_Layouts 로 빼 Node 진입점과 브라우저가
  같은 한 벌을 쓰게 함(structureAreaRows / applyStructureAreaRows).
- [저장]·[확정]이 카드를 그리지 않은 측점까지 면적을 계산해 cross_patch 로
  보냄. 유토곡선도 그 위에서 쌓음.
- 서버는 저장 때 Node 를 돌리지 않음 — 포장 구간·세월교 노면 하강 보정만
  남기고, 그 보정은 편집분을 얹기 전에 돌게 순서를 바꿈.

검증: tsc --noEmit 통과, pytest 387 passed, Node 진입점 스모크 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 14:59:31 +09:00
eomsangdonandClaude Opus 5 05a6b7e506 feat(B07): LibreDWG 변환기 동봉 및 자동 탐색
- 저장소에 LibreDWG 0.14.8594 Windows 실행 파일 동봉 (dwg2dxf/dxf2dwg + 필요한 DLL 4개, 약 33MB)
  실제 파일은 메인 워크트리 B07_DesignDetail/openwebcad/tools/libredwg 에 두고 보조 워크트리는 junction 으로 연결
- 파이썬 바인딩 미포함 (import 시 GPL 이 본 프로그램까지 확산) — 별도 프로세스 호출 방식 유지
- GPL v3 전문(COPYING)과 원본 출처·구성·교체 방법 README 동봉
- 변환기 탐색 순서: .env 경로 → 동봉본 → PATH
- 옛 DXF 의 유니코드 escape 를 복원해 DWG 왕복 시 한글 깨짐 해결, DWG 출력용 DXF 는 R2004 로 생성
- DWG 왕복 검증 테스트 추가 (tmp/tests, git 추적 제외)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 14:56:16 +09:00
eomsangdonandClaude Opus 5 a0e50d93e3 fix(B06): 상세 조회에서 계획을 다시 계산하던 자리를 저장 시점으로 옮김
사용자 확정(2026-09-06): 읽을 때는 영구저장소에서 가져오기만 하고, 계산은
화면(캐시)에서 하며, 저장·확정에서만 영구저장소에 쓴다.

- get_section_detail 이 조회할 때마다 돌리던 포장 구간 보정·세월교 노면 하강
  보정을 제거. 조회는 저장분을 그대로 싣는다.
- 두 보정을 recompute_server_side 로 옮겨 [저장]·[확정]·자동설계 체인에서
  정본에 남긴다. 보정된 설계 위에서 구조물 면적·유토곡선이 나오도록 순서를
  잡았다.
- 설계 미지정 측점 폴백(attach_default_designs)만 조회에 남긴다 — 저장분이
  있으면 아무 일도 하지 않는다.

검증: pytest 387 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 14:49:44 +09:00
eomsangdonandClaude Opus 5 2b6e76cd1c feat(B07): 도면 DXF/DWG 내보내기 추가
- 내보내기 엔진 신설 — 캐드 도면(JSON)을 ezdxf 로 DXF 작성(선·폴리선·글자·점·원·호, 도면층 유지), DWG 는 LibreDWG dxf2dwg 를 별도 프로세스로 호출
- POST /{project_id}/drawing-export 신설, 한글 파일명은 RFC 5987 방식으로 전달, 담지 못한 그림 수는 X-Aislo-Skipped 헤더로 통지
- CAD 출력 리본에 「DXF 내보내기」·「DWG 내보내기」 추가 — 캐드가 부모에 요청하면 부모가 서버 파일을 받아 내려받기
- 도각·내보내기 엔드포인트를 B07_DesignDetail_Router_Frame 으로 분리 (700줄 제한)
- 그림(Image)은 DXF 외부 참조 방식이라 제외하고 개수를 안내

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 14:38:55 +09:00
eomsangdonandClaude Opus 5 aad78ec942 feat(B06): 배수관 세트 제원 TS 짝 신설 — 브라우저에서도 계산 가능
B05·B06 계산을 서버·브라우저 양쪽에서 돌 수 있게 하는 작업의 1단계.
배수관 세트(배관·세월교·BOX암거·물넘이포장·독립 기슭막이) 제원이 서버에만
있어 브라우저는 스펙 칸을 하나씩 옮겨 적는 부분 사본으로 버티고 있었다.

- common_util_culvert_sets.ts 신설 — B06_Section_Engine_Culvert.py 의 짝.
  매 상세 조회마다 도는 자리라 Node 왕복 대신 짝을 택함(CLAUDE.md 5장 ①).
- 거울 테스트 추가 — 다섯 시설을 태워 측점별 딕셔너리째 대조.
- 흩어진 임시 산식 3곳(관경÷1000 · BOX 구체 길이 · 날개벽 바닥판 연장)을
  이 한 벌로 바꿈.

검증: tsc --noEmit 통과, pytest 387 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 14:32:55 +09:00
eomsangdon 1f3b30e698 @
feat(B06): 구조물 면적·유토곡선 서버 계산 자리 마련 + 상단측 저장 누락 수정

계산 자리 일원화(CLAUDE.md 5장) — 브라우저에서만 돌던 두 계산을 서버가 같은 TS 로
한 번 더 돌려 정본에 얹음. 파이썬 포팅 금지(기하가 두 벌이 되면 그림과 수량이 갈림).

- B06_Section_Server_Calc_Node.ts 신설 — 구조물 폐회로 면적 계산 후 그 위에서
  유토곡선을 쌓음(화면과 같은 순서). balloon 위치는 서버가 만들지 않음.
- B06_Section_Structure_Layouts.ts 신설 — 정본만 읽는 제어기 흉내를 B07 도면에서
  떼어 공용화. B07·서버가 같은 한 벌을 씀.
- common_util_node_bundle.py 신설 — 번들 빌드·실행 배관 공용화(코리도도 이걸 씀).
- 전처리 체인(초기값 스냅샷 앞)·[저장]·[확정]에서 서버 재계산 호출.
- 상단측(측구 방향) 변경이 B05 [임시저장]에만 실리던 것을 B06 [저장]·[확정]에도
  실음 — flushUphillOverrides.
- 죽은 세션 등록 항목 pipes 제거(읽는 곳도 쓰는 곳도 없었음).

검증: tsc --noEmit 통과, pytest 386 passed, tmp/tests/test_b06_server_calc_node.mjs 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@
2026-09-06 14:20:54 +09:00
eomsangdonandClaude Opus 5 ae719f22b2 feat(B07): DWG 변환기를 LibreDWG 로 교체·지원 버전 안내 추가
- ODA File Converter 제거 — 비회원 무료 사용이 비상업 용도로 제한되어 상용 서비스에 부적합
- LibreDWG dwg2dxf 를 별도 프로세스로 호출 (GPL 의무가 본 프로그램에 미치지 않는 방식), 경로는 .env LIBREDWG_DWG2DXF_PATH 또는 PATH 자동 탐색
- DWG 머리글로 형식 판정 — R2018 이후·미지원 형식은 변환 시도 없이 「2018 DWG 또는 DXF 로 저장」 안내
- 변환 실패·변환기 미설치도 같은 안내로 통일
- 형식 판정·안내 경로 테스트 추가 (tmp/tests, git 추적 제외)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 14:16:21 +09:00
eomsangdonandClaude Opus 5 6f201242be refactor(B07): 자리표 팔레트를 CAD 내부 패널로 이동
- 부모 사이드바 팔레트(B07_DesignDetail_UI_FramePlaceholders.ts) 제거, CAD 앱에 FramePlaceholderPanel 신설
- 도면 로드 메시지에 frameEdit 플래그 추가 — 도각 편집으로 실은 도면에서만 패널 표시
- 자리표를 화면 중앙에 직접 생성 (부모 왕복 없이 CAD 상태에 추가, 실행취소 스택 반영)
- 그림 자리표는 값이 없어도 테두리와 이름을 그려 배치 위치가 보이게 함

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:53:07 +09:00
eomsangdonandClaude Opus 5 784d6df801 feat(B07): 도각 외부 파일 불러오기(DXF/DWG)와 자리표 배치 도구
- DXF -> 도각 JSON 변환 엔진 신설 (ezdxf) — 선·폴리선·원/호/타원/스플라인 평탄화·글자·점 변환, 블록·치수는 분해, 해치 등 미지원 요소는 제외
- DWG 는 ODA File Converter(.env ODA_CONVERTER_PATH) 경유 변환, 미설치 시 DXF 저장 안내로 폴백
- POST /{project_id}/frame-template/import 신설 — 파일을 편집 화면용 도면으로 반환(저장은 기존 [완료] 경로 유지), 20MB·2만 도형 상한
- 도각 편집 띠에 「파일 불러오기」 추가, 자리표 팔레트 신설 (글자 14종·그림 4종을 도면 중앙에 배치 후 이동)
- CAD 텍스트 선택 시 회색 점선 외곽선 표시 (출력물에는 미포함)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:41:22 +09:00
eomsangdonandClaude Opus 5 dc52d55988 feat(B06): 구조물이 만든 폐회로로 절·성토 면적 산출
2026-09-06 사용자 확정 — 구조물 **자체 면적을 빼는 것이 아니라**, 지반선과 실제로
그려지는 설계선이 이루는 폐회로의 넓이다. 기존 계산은 표준 설계선만 보아 구조물이
있는지조차 몰랐다.

- 그리는 쪽이 이미 만든 트림(`designTrim`)을 그대로 받아 「실제로 그려지는 설계선」을
  세운다 — 트림 안쪽은 설계선, 바깥은 구조물 성토부선, 그것도 없으면 원지반(면적 0).
  그림과 면적이 같은 입력을 쓰므로 둘이 갈리지 않는다.
- 벽면은 **수직**이라 트림 경계 바로 바깥에 점을 하나 더 찍는다. 안 넣으면 사다리꼴이
  단차를 비스듬히 이어 붙여 없는 면적이 생겼다(실측 6.25㎡ → 7.5㎡).
- 절토는 암 경계선이 있으면 기존 규칙대로 토사/암으로 나눈다.
- 확인(node) — 전구간 100㎡ · 벽에서 끊음 6.25㎡ · 구조물선 포함 20㎡.

한계: 서버는 아직 구조물 기하를 몰라 이 보정이 **브라우저 계산에만** 실린다. 저장·확정
뒤 서버 재계산에서는 표준 값으로 돌아간다 — 서버 반영은 남은 작업(계획서 3-2).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:35:16 +09:00
eomsangdonandClaude Opus 5 39bb1e6754 refactor(B01): 저장소 700줄 분리 + 코드 검토 지적사항 수정
- B01_Dashboard_Repository 에서 회사·가입신청 부분을 B01_Dashboard_Repository_Company 로 분리 (740줄 -> 508줄)
- 팀원 초대 메일 본문의 회사명·이름 HTML 이스케이프 (임의 HTML 삽입 차단)
- 지도 타일 엔드포인트 z/x/y 범위 검증 추가
- 회사 정보 수정 시 회사명·사업자번호 중복은 409 로 응답 (기존 500)
- 값 변경 없는 회사 수정이 404 로 튕기던 문제 수정
- 팀원 미선택 시 안내 문구 한글화

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:22:40 +09:00
eomsangdonandClaude Opus 5 1bfc9ee12c fix(B06): 등록표에 extraspan 이 빠져 B06 화면이 통째로 안 뜨던 회귀
캐시·세션 일원화에서 조정창 값 8종을 옮기며 다단 구간값(`extraspan`)을 등록표에
빠뜨렸다. 표에 없는 이름을 꺼내다 예외가 나 페이지 렌더가 통째로 실패했고 화면에는
「준비 중인 페이지입니다」만 떴다(화면 확인으로 발견).

- `extraspan` 을 등록표에 넣음.
- 표에 없는 이름은 이제 **예외 대신 저장 건너뛰기** — 그 값만 안 남고 화면은 선다.
  콘솔에 한 번 알려 표에 줄을 넣게 한다.
- B06 이 키를 만들 때 쓰던 형변환을 없앰 — 앞으로 등록표에 없는 이름은 컴파일에서
  걸린다. 같은 사고가 화면까지 가지 않는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:21:32 +09:00
eomsangdonandClaude Opus 5 2b18c1b535 feat(B06): 기슭막이 연동 해제 시 높이도 분리 — 푸는 순간 값을 굳힘
2026-09-06 사용자 지시. 연동 해제는 위치(4축)만 갈랐고 높이는 소유 측점 값을 계속
따라가, 소유 측점 높이를 바꾸면 푼 측점까지 같이 움직였다.

연동을 푸는 그 순간 화면에 그려진 벽 높이를 이 측점 조정값으로 굳힌다 — 값이 튀지
않으면서 이후에는 따로 논다. 이미 손댄 벽은 그대로 둔다. 단 수·형태는 소유 측점을
따르는 2026-08-30 확정을 그대로 지킨다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:09:21 +09:00
eomsangdonandClaude Opus 5 283a248f89 feat(B06): 곡선부 노폭 자동 확폭 — 평면 R 기준, 곡선 바깥쪽 편측
2026-09-06 사용자 확정. 별표2 Ⅰ.2.나.(4) 확폭표(R 10~45m → 2.25~0.25m)를 측점별
평면 곡선반경에 물려 차도 폭을 넓힌다.

- 확폭 방향은 **곡선 바깥쪽 편측** — 노선 폴리라인의 외적 부호로 회전 방향을 보고
  바깥쪽을 정한다(좌회전이면 우측). 측점 기록에 `curve_outer_side` 로 실린다.
- 차도 반폭을 좌·우로 나눠 들어 한쪽만 넓어지게 함. 확폭이 0이면 예전과 같은 대칭
  단면이다. 노견·측구·사면은 그 바깥으로 그대로 밀린다.
- 확폭을 더한 유효너비는 법정 상한 5m 에서 자른다(규격 3.0m 면 최대 2.0m 까지).
- 계산 짝을 함께 고침 — 파이썬 `compute_cross_design` 과 브라우저 `computeCrossDesign`,
  표는 양쪽에 두되 짝임을 주석으로 못 박음. 확폭 입력은 측점 기록에서 뽑는 헬퍼
  하나로 9개 호출부(횡단·확정·B07 도면)에 같은 값이 가게 함.
- 횡단도에 노폭 라벨 — 확폭이 걸리면 「노폭 4.5m (규격 3.0 + 확폭 1.5)」로 적는다.
- 확인: 표 경계·편측 적용·5m 상한·회전 방향 판정 5건(pytest) + 브라우저 표 15건 일치.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:07:56 +09:00
eomsangdonandClaude Opus 5 c7099bd1d3 feat(B05,B06): 측점별 평면 곡선반경 산출 + 법정 최소반경 경고, 자동탐색 입구 내림
2026-09-06 사용자 확정. 자동탐색은 평면만 보고 노선을 정해 실제 판단과 맞지 않으므로
쓰지 않는다 — 버튼만 감추고 코드·API 는 남긴다. 대신 법정 평면 기준을 **경고**로 낸다.

- 횡단 생성이 측점마다 평면 곡선반경(`plan_radius_m`)을 낸다. 노선 폴리라인 위에서
  앞뒤 10m 떨어진 세 점의 외접원 반경이며, 직선(1만m 이상)은 null. 반지름 50m 원호로
  50.008 이 나오는 것을 확인.
- 법정 최소곡선반지름 표(별표2 Ⅰ.2.다.(1) — 40:60/40, 30:30/20, 20:15/12)와 배향곡선
  하한 10m 를 설정에 넣고 계획선 정책에 실어 화면으로 내린다. 탐색이 쓰는 등급별
  상수와는 별개다.
- 종단 상단줄에 「곡선반경 부족 n곳 / 최소 R」 경고 추가 — 자동 보정·차단은 없다.
  툴팁에 하한값과 배향곡선 하한 미만 개수를 적는다.
- 이 반경은 다음 작업(곡선부 확폭)이 그대로 쓴다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:55:22 +09:00
eomsangdonandClaude Opus 5 6d9c7087e9 fix(B05,B06): 3D 에서 고른 구조물이 B06 횡단도에 안 뜨던 문제 — 세 자리 수리
2026-09-05 사용자 지시 건. 넘김 자체는 서 있었고 값이 중간에 지워지거나 대조에서
떨어지고 있었다.

- 부재키가 지워지던 길 — 3D 로 고른 뒤 선택 동기화가 **측점만** 다시 적어 부재키를
  덮었다. 같은 측점이면 이미 적힌 부재키를 지키게 함(모든 호출자에 함께 적용).
- 측점 대조 허용오차 0.01m → 0.05m. 같은 화면의 다른 대조와 같은 값이며, 3D 가 준
  누가거리가 소수점 아래에서 조금만 달라도 측점을 못 찾고 조용히 끝나던 원인.
- 「진입당 한 번」 빗장을 넘김값 서명 비교로 교체 — 자료가 늦게 와도 다시 서고,
  같은 값이면 재렌더에도 스크롤이 안 튄다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:50:00 +09:00
eomsangdonandClaude Opus 5 a126df25bb refactor(B05,B06): 설계 초안·계산 결과도 등록표로 — 이동할 때 다시 부르지 않게
캐시·세션 일원화 2단계 (2026-09-06 사용자 지시).

- ② 설계 초안 이관 — 구조물 미저장분, 상단측 변경분, 페이지 간 넘김값(구조물 선택),
  표준횡단 편집값, 조정창 4축·단수·연동값 8종, 암 경계선, 표시 반폭. 넘김값은 별도
  임시 키를 없애고 초안 한 자리로 흡수.
- ④ 계산 결과 이관 — 최신 노선 응답, 서버가 준 규격 횡단·암 경계 기본값. 횡단 상세는
  메모리 캐시에 더해 세션에도 한 벌 얹어 새로고침 뒤 첫 화면이 서버를 안 기다림
  (용량을 넘으면 조용히 건너뛰고 예전대로 다시 받음).
- B05 진입의 강제 새로읽기를 걷어냄 — 캐시로 먼저 그리고 노선번호·지표면 서명으로
  뒤에서 신선도만 확인, 달라졌을 때만 다시 그림.
- [초기화]의 초안 비우기를 `clearDrafts` 한 곳으로 모음(예전에는 파일마다 따로).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:48:40 +09:00
eomsangdonandClaude Opus 5 2c03dbee3e fix(B01): 값 변경 없이 참여자만 저장할 때 404 반환 문제 수정
- update_project 가 rowcount 0(변경된 컬럼 없음)을 프로젝트 없음으로 오판하던 자리에 존재 확인 추가
- 참여자 수정권한·시스템 회사 역할 규칙 검증 테스트 추가 (tmp/tests, git 추적 제외)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:46:11 +09:00
eomsangdonandClaude Opus 5 05a63c8440 refactor(B05,B06): 화면 상태 보관소를 한 곳으로 — 등록표 신설 + 취향값 이관
2026-09-06 사용자 지시(캐시·세션 일원화 1단계). 세션 접근이 19개 파일 64곳에
흩어져 있고 키 이름이 네 갈래라 같은 설계값인데 두 화면이 서로의 값을 못 보는
자리가 있었다.

- `A00_Common/b_page_state.ts` 신설 — 값마다 통(① 취향 · ② 초안 · ④ 계산 결과)과
  범위(전역·프로젝트·노선)를 한 줄로 적는 등록표. 키 형식은
  `aislo:{통}:{이름}:{프로젝트}[:{노선}]` 하나이고 **페이지 이름을 키에 넣지 않는다**.
- 옛 키는 처음 읽을 때 새 키로 한 번 옮기고 지운다(사용자가 쓰던 배치 유지).
- 초안 비우기(`clearDrafts`)·결과 버리기(`clearResults`)를 그 한 곳에 둠.
- 이번 커밋은 ① 취향값 11개를 이관 — 패널 접힘·높이, 유토곡선 펼침·높이·범례,
  테이블 펼침·높이, 배수유역 접힘·폭, B06 패널 접힘·높이.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:42:19 +09:00
eomsangdonandClaude Opus 5 9f36b5fab4 feat(B05): 유토곡선 총괄값을 종단 상단줄로 — 곡선을 접어도 남게
2026-09-06 사용자 지시. 요약 막대가 유토곡선 오버레이 안에 있어 패널을 접으면
같이 사라졌는데, 계획선을 만지며 봐야 하는 값이라 상단줄로 올림.

- 상단줄 「최대 기울기」 오른쪽에 「누가토량」 추가. 값은 유토곡선 계산 결과의
  마지막 누계이며 토량분배 켜짐과 무관하다. 음수(부족)면 경고색.
- 세부(절토 자연·다짐, 성토, 잉여/부족)는 그 항목 툴팁으로 내림 — 예전 요약
  막대가 세부를 툴팁에 두던 방식 그대로.
- 유토곡선 오버레이의 요약 막대 행 삭제. 비면 접히게 해 곡선이 그만큼 넓게 씀.
- 총괄값은 패널이 접혀 있어도 계산해 넘김(draw 앞단에서 산출).
- 상단줄 높이 24 → 28px.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:37:59 +09:00
eomsangdonandClaude Opus 5 aed7465a5f feat(B01,B02): 프로젝트 참여자 다중 지정·가입요청 목록 정리·서명 문구 개선
- project_members 표 신설(016 마이그레이션, 기존 프로젝트는 생성자 자동 채움)
- 프로젝트 수정 모달에 참여자 선택 추가, 참여자로 지정된 일반사용자는 해당 프로젝트 수정 가능 (백엔드 _can_edit_project 동반 수정)
- B02 프로젝트 생성 시 생성자를 참여자로 자동 등록, 참여자도 같은 회사 구성원인지 검증
- 가입 요청 목록을 대기 상태만 표시 (처리 완료 건이 사용자 관리 목록과 중복 노출되던 문제)
- 팀원 등록 시 남아 있던 가입 신청 자동 정리
- 서명·로고 선택 문구를 뜻이 드러나게 수정

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:33:18 +09:00
eomsangdonandClaude Opus 5 fdcd075396 fix(B05): 3D 노선 선이 능선에서 끊겨 보이던 문제 — 2m 간격으로 지형에 붙임
원인은 선의 해상도 (2026-09-06 실측). 노선 정점(간격 중앙값 10.0m)을 곧바로
이어 지형 위 0.35m 로 띄웠는데, 볼록한 능선에서는 그 10m 직선이 지면을 뚫고
들어가 선이 땅속에 잠겼다 — 화면에서는 161+0.0~163+0.0 구간이 통째로 사라졌다.

정점 사이를 2m 간격으로 나눠 각 점의 지형고를 찍는다. 원래 정점은 모두 남기므로
평면 형상은 불변. 끼운 점과 원래 정점이 같은 지형면에서 높이를 받도록 지형고를
먼저 보고, 없을 때만 정점 z 를 쓴다. 곡선 경고 구간은 원래 정점 기준 인덱스라
조밀화 뒤 자리로 옮겨 자른다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:32:29 +09:00
eomsangdonandClaude Opus 5 3def4f18f0 feat(B05): 종단 그래프·유토곡선·측점 테이블 끌어서 이동
2026-09-06 사용자 지시. 좌클릭·휠 버튼으로 그림 위에서 바로 잡아 끈다.
공용 스크롤러 한 곳에 넣어 세 곳이 함께 얻음. 4px 문턱을 넘긴 뒤에만 이동으로
보고 그 경우에만 뒤따르는 click 을 한 번 삼켜, 측점 선택·선택 해제는 그대로 둠.
계획고 편집 버튼·리사이저 위에서 시작한 끌기는 제외(구조물 마크는 자기
pointerdown 에서 전파를 끊어 이미 제외됨). 끌기 시작 시 휠 관성 이동은 중단.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:31:11 +09:00
eomsangdonandClaude Opus 5 d3d0dcaa0e feat(B05,B06): 유토곡선 초기 화면은 곡선만 — 토량분배 기본 꺼짐
2026-09-06 사용자 지시. 기본 켜짐 목록에서 토량분배(평형선·운반 블록)를 빼고
세션 저장 키 판을 v4 → v5 로 올림 — 안 올리면 이미 켜 둔 브라우저가 옛 값을
그대로 씀. 범례에서 켜면 예전대로 나옴. 두 화면이 같은 키를 쓰므로 두 벌을
함께 고침.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:29:36 +09:00
eomsangdonandClaude Opus 5 bca78ee6bd feat(B01): 회사 정보 수정 화면·중복 조회·주소 지도 미리보기
- 회사 정보 수정 API(PUT /dashboard/company) 및 수정 모달 신설, 회사 패널·회사 관리 표에 수정 진입점 추가
- 회사 등록·수정 입력칸 공용화 및 순서 조정 (사업자등록번호 > 회사명 > 대표자명 > 로고 > 주소)
- 회사 등록 시 사업자번호·회사명으로 기존 회사 조회, 있으면 가입 신청으로 유도
- 주소 지도 미리보기 추가 — VWorld 주소검색·배경지도 타일을 서버가 중계(키 노출 방지), 지도 라이브러리 없이 타일 3x3 조합

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:29:00 +09:00
eomsangdon 54478394f4 auto: 2026-09-06 11:28 (ESD_LAPTOP) 2026-09-06 11:28:25 +09:00
eomsangdonandClaude Opus 5 4cbfb7e690 feat(B05): 종단 테이블 12행 → 6행 정리
화면 자리 확보 목적 (2026-09-06 사용자 지시). 뺀 6행 = 구배 L·H, 절토고,
성토고, 누가거리, 거리. 남긴 6행 = 구배 S, 계획고, 지반고, 측점, 곡선 L·R.
뺀 값은 사라지지 않음 — 구배 L·H 는 블록 툴팁, 절·성토고와 누가거리는 선택
측점 값 열 툴팁에 남김. B07 도면 종단도는 별도 코드라 12행 그대로.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:28:23 +09:00
eomsangdonandClaude Opus 5 2adb5507a4 feat(B01,B02): 팀원 등록 방식 교체·사용자 삭제/비활성화·B02 등록 화면 정비
B01
- 팀원 등록을 계정 대리 생성에서 「소속 없는 기존 가입자 검색·선택」으로 교체, 미가입자는 안내 메일만 발송
- 사용자 계정 삭제 API 신설, 삭제 모달에서 회사 제외/계정 삭제 선택, 관리자 수정 모달에 활성·비활성 상태 지정
- 기본정보 폼과 사용자 수정 폼을 공용 입력칸 한 벌로 통일 (이름 > 직급 > 이메일 > 부서 > 전화)
- 프로젝트 담당자 선택의 신규 등록 항목 제거

B02
- 프로젝트명 자동 조합 (사업연도 + 사업지역 + 임도종류 + 직접 입력값) 및 미리보기 표시
- 예상 연장 입력칸 제거, 노선 종료 누가거리 제목에 구간 연장 표기, 표지 값은 프로젝트명·연장에서 파생
- 취소 버튼 추가 (대시보드 복귀), 담당자 기본값을 생성자로 지정

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:24:43 +09:00
eomsangdonandClaude Opus 5 5502f95040 feat(B01): 시스템 회사 자동 권한·회사관리자 권한 확대·재신청 방어
- 시스템 관리 회사(.env 관리자 계정 소속 회사) 소속은 전원 시스템관리자 — 서버 시작 시 일괄 반영, 가입 승인·팀원 등록 시에도 자동 부여
- 이미 소속이 있는 사용자의 회사 가입 재신청 차단 (기존: 신청 즉시 소속 해제·승인대기 전락)
- 회사관리자에게 자기 회사 역할 변경·프로젝트 삭제 허용, 마지막 관리자 강등 차단
- 역할 변경 시 is_master 동반 조정 (권한 판정 불일치 제거)
- 일반사용자도 소속 회사 프로젝트 전체 열람 (수정 권한은 별개)
- 대시보드 시스템 로그 섹션 최하단 이동
- PLAN.md 에 B01 정비 계획 반영

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:16:25 +09:00
eomsangdonandClaude Opus 5 c3dc1f4395 chore(config): 구글 토큰 자동 갱신값 반영
- google_token.json 액세스 토큰·리프레시 토큰 갱신
- scopes 에 script.projects 추가

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 07:43:02 +09:00
eomsangdon 3a4d3eebad auto: 2026-09-05 23:08 (ESD_LAPTOP) 2026-09-05 23:08:21 +09:00
eomsangdon 236dcb7e88 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-05 08:47:07 +09:00
eomsangdon 4d79e65821 Merge remote-tracking branch 'origin/main_desktop_1' into main_laptop_1 2026-09-05 08:46:08 +09:00
eomsangdon 40b69804d2 Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-05 08:44:35 +09:00
eomsangdon 77c9dac81b auto: 2026-09-05 08:44 (EOMSANGDON-HOME) 2026-09-05 08:44:30 +09:00
eomsangdon 97cc6f6726 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-04 23:46:15 +09:00
eomsangdonandClaude Opus 5 5446f0cfd9 fix(B05): 구조물 측점 계획고 편집이 가짜 변화점을 만들던 것 — 정본 누가거리 일원화
사용자 보고(2026-09-04) — 배수관 2 자리에서 계획선이 수직으로 꺾이고 최대 기울기가
**155,791 %**. 원인은 「가까운 점」이 아니라 **한 구조물이 층마다 다른 누가거리를 든 것**:
  횡단 측점 264.054626 · 종단 정본 변화점 264.055 · 배수 정본 파일 **264.06**(cm 반올림).
사이드 목록이 든 264.06 으로 계획고를 편집하면 정본 옆 5mm 자리에 변화점이 하나 더 서고,
둘 사이 종단곡선이 mm 로 쭈그러들어 기울기가 거리 0 에 가까운 값으로 나뉘었음.
2026-09-03 에 ▲▼ 버튼 경로만 막아 뒀고(같은 증상 11,858%), 측점 테이블·도구 경로가 남아
다시 터진 것.

- **저장**(`common_util_drainage_pipes.py`) — 누가거리를 좌표(x·y)와 같은 **밀리미터 기준**으로
  남김(cm 반올림 제거). 종단 정본 변화점(3자리)과 값이 어긋나지 않음.
- **화면**(`_UI_Profile_Panel.ts`) — 구조물 측점 목록이 들어오는 **한 자리**에서 종단 정본
  측점(0.1m 안)으로 누가거리를 갈아 끼움. 버튼·테이블·도구 **모든 편집 경로**가 함께 막힘.
  이미 cm 로 저장된 옛 프로젝트도 이 경로로 안전해짐.

자체검증(공용 브라우저 임시 탭):
① 264 부근 그래프 측점이 **264.055 하나**로 모임(고치기 전 264.06 이 따로 있었음)
② 그 구조물 측점 계획고를 8칸 올려도 **수직 꺾임 0건**, 계획선 점 수 1,208 불변,
   최대 기울기 66.0 → 183.5%(8칸 올린 만큼의 정상값. 고치기 전 155,791%)
③ 전체 시험 381 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 23:33:32 +09:00
eomsangdonandClaude Opus 5 4a57272e72 fix(B05,B06): 종단 그래프를 왔다갔다하면 Y축 눈금이 비던 것
사용자 보고(2026-09-04) — 「종단 그래프 왔다갔다하면 Y축 값이 비는 경우가 있음」.
세로창을 변환으로 옮기면서 눈금을 **배율이 크게 바뀔 때만** 다시 만들게 해 뒀는데,
창이 위아래로 **이동만** 하면 배율은 그대로라 눈금이 한쪽으로 쓸려 나가고 그 자리가 빔.

- 새 창에 있어야 할 눈금 수와 화면에 남는 수가 다르면 눈금을 다시 만들게 함(요소 20개
  안팎이라 값싸다). 배율 문턱 판정은 그대로 둠.

자체검증(공용 브라우저 임시 탭): 확대 4칸 뒤 좌우로 6번(+900·+900·−600·+1400·−1800·+2400px)
오가며 눈금이 플롯을 덮는지 측정 — 눈금 6~11개가 항상 이어지고 **위·아래 빈틈 최대 37px**
(플롯 높이 329px, 창 여유 20%에 해당). 고치기 전에는 플롯 절반이 비었음.
전체 시험 381 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 23:19:07 +09:00
eomsangdonandClaude Opus 5 79dc0cc82d fix(B05,B06): 계획고를 조절해도 횡단 기준 유토곡선이 안 따라오던 것 — 계산 캐시 제거
사용자 보고(2026-09-04) — 「종단 곡선 조절에 따라 횡단 기준 유토곡선 변경 안 됨」.
오늘 프레임을 아끼려고 넣은 곡선 계산 캐시가 원인. 횡단 설계는 `refreshCrossDesigns` 가
**같은 객체를 제자리에서 고치므로**(배열·요소 동일성 유지) 「입력 객체가 같으면 안 바뀐 것」
이라는 캐시 판정이 성립하지 않음. B06 은 `detail` 동일성으로 캐시해 **한 번 계산한 뒤로는
영원히 낡은 곡선**을 보였음.

- 두 곳(B05 패널·B06 조립기)의 캐시를 걷어내고 그릴 때마다 계산하게 되돌림.
- 왜 캐시하면 안 되는지 주석으로 못 박음.

자체검증(공용 브라우저 임시 탭):
① B06 — 횡단 지반 종류를 바꾸자 절토 4,500.3 → 4,500.7㎥, 토취 10,211.5 → 10,211.1㎥,
   곡선 경로도 바뀜(고치기 전에는 완전 동일)
② B05 — 계획고 6칸 올리자 계획선·유토곡선 둘 다 바뀜(성토 14,729 → 17,513㎥)
③ 휠 20칸 기준 최대 프레임 27.0ms, 33ms 초과 **0건**(캐시 없어도 예산 안)
④ 전체 시험 381 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 23:10:22 +09:00
eomsangdonandClaude Opus 5 0ea75e512f style(B03): 계획노선 미리보기 — 범위 점선 상자 제거, 실선 녹색
- 계획노선처럼 그릴 선이 있는 자료에는 범위 점선 상자를 그리지 않음.
  점구름·래스터는 그대로 상자를 그림.
- 노선 선 색을 녹색으로. 지형 범위를 벗어나면 붉은 경고색으로 덮어쓰는
  규칙은 유지.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 22:58:45 +09:00
eomsangdon 27751d76ad Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-04 22:50:22 +09:00
eomsangdonandClaude Opus 5 5666086c6e fix(B03): 파일 교체 후 다시 업로드할 수 있게 — 끝난 카드 [파일 선택] + 확정 대기 관문
- 완료 카드에서도 [파일 선택]을 보임. 올리는 중에만 감춤. 교체 확인 모달과
  교체 업로드 규칙은 이미 있던 것을 그대로 씀.
- 업로드 관문이 전처리 1단계 IN_PROGRESS 를 무조건 「분석 중」으로 읽어,
  자동 확정이 보류된 프로젝트는 영영 새 자료를 받지 못했음. 전처리 진행 파일이
  awaiting_confirmation·completed·failed 로 끝나 있으면 받도록 고침.
  실제로 도는 중(analyzing)이면 그대로 막음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 22:42:57 +09:00
eomsangdonandClaude Opus 5 2b1fe118d1 fix(B05,B06): 유토곡선 세로창 버티기 + 부드럽게 이동
사용자 확정(2026-09-04) — 「버티기 + 부드럽게」. 누가토량 곡선은 표고와 달리 가팔라,
급한 구간이 창에 들어오면 세로 폭이 한 칸에 228 → 1,019㎥(4.5배)로 튀었음(실측).

- **버티기** — 곡선이 지금 창 안에 들어오고 창을 55% 넘게 채우면 창을 아예 안 건드림.
- **부드럽게** — 바꿔야 할 때도 한 번에 안 가고 다시 그릴 때마다 남은 만큼 20%씩 좁힘
  (약 0.15초에 걸쳐 미끄러짐). 아직 안 끝났으면 다음 프레임에 한 걸음 더.
- 여유를 5% → **20%** 로 넓혀 곡선이 위아래 끝에 딱 붙지 않게 함(종단과 같은 값).
- 곡선 계산(`computeMassHaulSeries`)을 입력 객체 단위로 아껴 둠 — 스크롤 중에는 입력이
  그대로인데 그릴 때마다 다시 계산해 프레임을 먹었음.

자체검증(공용 브라우저 임시 탭):
① 한 칸씩 8번 굴려 세로 폭 257·257·257·257 → 1,031 … : **4칸은 아예 그대로**(버티기)
② 창이 바뀌는 순간 궤적 257 → 711(42ms) → 882(80ms) → 1,030(151ms): 계단이 아니라 미끄러짐
③ 휠 20칸 기준 최대 프레임 27.4 → **24.0ms**, 33ms 초과 **0건**(고치기 전보다 나아짐)
④ B06 최대 프레임 21.2ms, 33ms 초과 0건 ⑤ 전체 시험 381 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:40:41 +09:00
eomsangdonandClaude Opus 5 331be116a9 fix(B05,B06): 유토곡선이 그래프 칸 밖으로 새던 것 차단
사용자 보고(2026-09-04) — 「0선이 갑자기 위로 넘어가고 그 영역 채움색이 눈금 상한을 넘어
표현됨」. 보이는 구간에 세로를 맞추면서 0이 창 밖으로 나가는데, 유토곡선에는 **자르는
영역(clip)이 없어** 0선과 면 채움색이 범례·토글 버튼 위까지 그대로 칠해짐. 종단 그래프는
진작 자르고 있었는데 유토곡선만 빠져 있었음.

- 그래프 칸(축 오른쪽 · 상단 여백 아래)을 자르는 겹 둘을 둠 — 측점 세로선이 면과 곡선
  **사이**에 서야 해서 앞뒤로 나눔(그리는 순서 유지).
- 면·곡선·0선·EP 표식·선택 강조·말풍선·토량 분배를 그 겹 안으로 옮김.
- 0선은 창 안에 있을 때만 그리고 눈금에도 그때만 실음 — 밖으로 나갔는데 실으면 좌측
  고정 축 맨 위에 남의 값과 겹쳐 찍힘(사용자 확정: 0선은 나가도 됨).
- 눈금 글자는 축 **왼쪽**이라 자르는 겹에 넣지 않음(넣으면 사라짐).

자체검증(공용 브라우저 임시 탭): 확대 4칸 + 휠 25칸으로 0을 창 밖으로 보낸 상태에서
① 자르는 겹 2개 확인 ② 0선·0눈금 사라짐, 눈금 5개 전부 음수(겹쳐 찍힌 0 없음)
③ 영역 캡처로 면 채움색이 그래프 칸 위 경계에서 정확히 멈추는 것 확인.
전체 시험 381 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:31:09 +09:00
eomsangdon 057ba34f32 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-04 22:18:36 +09:00
eomsangdonandClaude Opus 5 41c1771fad feat(B03): 부속 파일 카드에 파일 속 값 표시·래스터 썸네일 점선 테두리
개수만 보이던 부속 파일 카드에 파일 안의 중요한 값을 함께 보임.

- .shx — 도형 종류(폴리라인 등)와 범위 크기를 머리글에서 읽음.
- .dbf — 속성 이름 목록을 머리글 서술자에서 읽음(앞 4개).
- .cpg — 같은 세트 .dbf 의 첫 글자 속성을 그 인코딩으로 읽어 견본으로 보임.
- .prj — 중앙자오선(투영 매개변수)과 길이 단위. proj 문자열 변환을 쓰지 않아
  정보 손실 경고가 나지 않음.
- 카드 표시 줄 수를 2 → 3 으로 늘리고, 길이 단위 표기를 m 으로 통일.
- GeoTIFF 썸네일에 다른 미리보기와 같은 점선 테두리.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 22:16:14 +09:00
eomsangdonandClaude Opus 5 685d44ac39 perf(B05,B06): 유토곡선·B06 페이지도 세로 창을 실시간으로 따라오게
사용자 지적(2026-09-04) — 「유토곡선도 반영해야지. B06 페이지도 있잖아. 같은 로직일 텐데
왜 반영이 안 된 거야」. 그리는 쪽(종단 렌더러)은 공용이라 이미 바뀌어 있었고, **스크롤을
받아 창을 옮기는 쪽이 화면마다 따로**라 B05 만 연결돼 있던 것.

- 세로 창 모듈을 `common_util_chart_ywindow.ts` 로 옮겨 B05·B06 이 함께 씀.
  좌측 고정 축은 표식(`b05-profile__yaxis--ywindow`)이 붙은 것만 옮김 — B06 은 한 컨테이너에
  종단·유토곡선 축이 함께 살아 남의 축까지 옮기면 안 됨.
- 세로 과장(B06 조절값)을 각인·반영 — 예전에는 과장이 1일 때만 대상이었음.
- 유토곡선은 변환이 아니라 **곡선만 다시 그림**(스크롤마다). 곡선 위 말풍선·EP 표·측점
  점처럼 세로로 늘어나면 안 되는 것이 많아, 그것만 따로 옮기는 값이 다시 그리는 값보다 큼
  (유토 SVG 234개 = 종단의 1/3).
- B06 상단 패널을 통째로 다시 그리던 스크롤 처리를 걷어내고, 종단은 변환·유토곡선은
  제자리 교체로 바꿈. 700줄 제한에 맞춰 종단 조립을 `_View_Chart.ts`(116줄)로,
  유토곡선 조립기를 `_View_MassHaul.ts` 로 분리(727 → 718줄).

자체검증(공용 브라우저 임시 탭):
① B05 유토곡선 눈금이 스크롤을 따라 4.3㎥ → −1,348.8㎥ 로 이동(예전에는 안 따라옴)
② B06 종단 눈금 830/840/850m → 824/826/828m, 유토곡선 68.2㎥ → −3,375.1㎥ 로 동시 이동
③ B06 최대 프레임 **35.9 → 23.5ms**, 33ms 초과 1 → **0건**(중앙값 16.7ms 유지)
④ B05 최대 프레임 27.4ms, 33ms 초과 0건 ⑤ 전체 시험 381 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:13:03 +09:00
eomsangdonandClaude Opus 5 8dca4bbfb3 perf(B05): 종단 세로 맞춤을 다시 그리기에서 변환으로 — 스크롤 중 실시간
사용자 지시(2026-09-04) — 「화면을 리프레시하는 느낌. 가장 강한 지연은 멈춘 뒤 끊김.
실시간처럼 되려면 근본적으로 표현을 바꿔야 하지 않나」. 확정: 변환 방식.

원인 — 세로 위치가 선 하나하나의 좌표에 박혀 있어, 세로 창이 바뀌면 그래프를 통째로
다시 만들 수밖에 없었음(SVG 요소 457개, 실측 34ms = 60Hz 두 프레임 누락).

- 세로에 딸린 도형(눈금선·절성토 음영·지반선·계획선)을 `b06-chart__ywindow` 한 겹으로
  묶고, 세로 창을 `translate`+`scale` 한 줄로 표현. 두 창의 대응이 1차식이라 정확히 겹침.
- 자를 영역(clip)은 변환 밖, 글자는 변환 밖에서 자리만 옮김(늘어남 방지).
  글자 내림 4px 은 변환 뒤에 더함 — 배율과 함께 커지면 선에서 떨어짐(실측 6.4px).
- 선 굵기·점선 간격은 `vector-effect: non-scaling-stroke`(변환이 걸렸을 때만 적용해
  B06 은 예전 그대로).
- 눈금 간격이 어긋나면(배율 0.8~1.25 밖) **눈금층만** 다시 만듦(요소 20개 안팎).
  눈금은 그릴 때의 좌표계에 놓아 같은 변환이 그대로 먹음.
- 눈금 계산을 `yWindowTickValues` 로 빼 그리는 쪽과 갱신 쪽이 같은 눈금을 씀.
- 패널은 스크롤마다 변환 갱신만 부름. 전체 재구성은 눈금으로도 못 살릴 때만 남김.

자체검증(공용 브라우저 임시 탭, 휠 20칸 4,800px):
① 전체 재구성 **0회**(고침 전 다회) ② 최대 프레임 **59.9 → 20.0ms**, 20ms 초과 0건
③ 눈금 글자와 눈금선 간격 6.4 → **0.5px** ④ 지반선의 한 점을 변환으로 놓은 자리와
눈금이 말하는 자리가 **0.000px** 일치(배율 2.715 상태) ⑤ B06 종단은 변환 겹 3개 모두
변환 없음(항등) — 화면 불변 ⑥ 전체 시험 381 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 21:56:25 +09:00
eomsangdonandClaude Opus 5 db3503fd95 fix(B05): 휠 이동 감속을 프레임 수가 아닌 흐른 시간 기준으로
프레임당 고정 비율(0.35)로 좁히면 주사율에 따라 속도가 달라짐 — 120Hz 화면에서는 두 배
빨라 툭 끊겨 보이고, 프레임을 한 번 놓치면 그만큼 덜컥 뜀. 흐른 시간으로 환산해
어느 화면에서나 같은 시간에 도착하게 함. 탭이 잠들었다 깬 큰 간격은 64ms로 자름.

자체검증(공용 브라우저 임시 탭, 휠 20칸): 프레임 중앙값 16.6ms(60fps) 유지,
최대 프레임 47.6 → 34.3ms, 20ms 초과 프레임 3 → 2건.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 21:31:16 +09:00
eomsangdonandClaude Opus 5 c322f53845 feat(B05): 코리도를 전처리 체인이 미리 만들어 영구저장 + 초기값 편입
사용자 확정(2026-09-04) — 「다른 로직과 동일하게: 전처리 계산 마지막에 연산 후
영구저장, 진입 시 로딩, 조작은 캐시, 저장·확정 때 저장」. 예전에는 사용자가 B05에
처음 들어간 그 순간 브라우저가 17MB 규모를 만들어 첫 진입이 느렸음.

계산은 **재구현하지 않음** — 브라우저가 쓰는 TS 빌더(8,209줄)를 Node 로 그대로 돌림.
- `B05_Profile_UI_Corridor_Envelope.ts` 신설 — 저장 형식·버전 해시·직렬화를
  `_UI_Corridor.ts` 에서 그대로 떼어 브라우저·서버 공용으로 둠(내용 불변).
- `B05_Profile_Corridor_Node.ts` 신설 — 입력 JSON 을 받아 저장본을 내놓는 진입점.
  `npm run build:corridor` 로 번들(335kB), `npm run build` 에 물림.
- `B05_Profile_Corridor_Prebuild.py` 신설 — 상세·노선점을 모아 Node 실행 후 저장.
  번들이 TS 원본보다 낡으면 스스로 다시 만듦(두 그림이 갈라지는 것을 막는 장치).
- 체인 마지막(스냅샷 직전)에 호출. 실패는 비치명적 — 저장본이 없으면 브라우저 폴백.
- 초기값 — 스냅샷이 코리도를 `initial_corridor.json` 으로 함께 뜨고, [초기화] 복원 때
  **새 route id** 이름으로 되돌림(복원이 새 번호를 만들기 때문).
- `window.__corridorSource` 디버그 훅 — 저장본을 썼는지 다시 만들었는지 화면 밖 확인용.

자체검증: 전체 시험 381 통과·17 건너뜀(옛 테스트 2건은 이름·경로 변경에 맞춰 갱신).
실경로 — 용화 프로젝트(route 139)로 사전 생성 성공, 브라우저가 그 저장본을
`source: "stored"`, 해시 `377c2a84` 로 **그대로 채택**(재빌드 0회, 전송 278ms).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 21:22:27 +09:00
eomsangdonandClaude Opus 5 c2f681f9d3 fix(B05): 종단·유토곡선 휠 이동 부드럽게 + 재계산 표시 오버레이화
- 세 곳(종단 그래프·유토곡선·측점 테이블)에 복사돼 있던 휠 처리를
  `B05_Profile_UI_Profile_Wheel.ts` 한 벌로 통합.
- 이동은 rAF 로 목표를 쌓아 프레임마다 좁힘 — `scrollLeft` 직접 대입의 120px 점프 제거.
  브라우저 `scrollTo({behavior:"smooth"})` 는 휠마다 애니메이션이 처음부터 다시 시작돼
  1.6초에 17px 만 움직여 쓰지 않음(실측).
- 세로 자동 맞춤 디바운스 160 → 80ms.
- 「다시 계산 중…」 을 요약 막대에서 떼어 곡선 영역 우측 상단 오버레이로 이동 —
  막대 줄바꿈으로 곡선이 밀리던 창밀림 제거.

자체검증(공용 브라우저 5173, 임시 탭): 휠 10칸 1,200px 중 1,199px 이동·궤적 연속,
마지막 휠 500ms → 재구성 813ms. 칩 표시/숨김에서 요약 막대 높이 53px·곡선 자리
(top 1078·height 226) 동일 — 창밀림 0. 칩은 곡선 영역 안 우측(범례 아래 4px).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 21:07:55 +09:00
eomsangdonandClaude Opus 5 973ac7138f fix(B07): 횡단 장 박스 간격 제거·리본 세로 스크롤 제거·좌측 패널 라벨 정리
- 횡단면도 장 배치의 칸 여백 8mm → 0mm — 이웃 박스 테두리가 맞닿음.
- CAD 리본 높이 116px → 122px — 내용(85px)이 보이는 높이(81px)를 넘어 생기던
  세로 스크롤바 제거. 캔버스 상단이 6px 내려감.
- 장 라벨을 누가거리 범위 「1060~1070m」에서 시작 측점 「No.53」으로 교체.
- 좌측 패널 2열 격자 단추의 최소폭을 풀어 패널을 14px 넘어가던 것을 막고,
  이름이 길면 `…` 로 잘리게 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 20:46:30 +09:00
eomsangdon 439a9e630a Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-04 19:36:43 +09:00
eomsangdon 454fa651f3 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-04 19:36:41 +09:00
eomsangdon 47da90bf71 Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-04 19:28:57 +09:00
eomsangdon 258b6ab2e4 Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1 2026-09-04 19:18:02 +09:00
eomsangdon 4e1cdafab7 Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-04 19:17:59 +09:00
eomsangdon 910da99b6d Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1 2026-09-04 19:17:14 +09:00
eomsangdonandClaude Opus 5 9019ca28ef feat(B03): 파일 카드 미리보기 확대 — 전폭·고정 높이, 라이다 점 그림·GeoTIFF 썸네일
- 미리보기를 카드 한 행 전폭·높이 120px 고정으로 키움.
- 라이다: 분류 통계를 훑는 그 길에 XY 를 성기게 주워 탑뷰 점 그림(상한 5천 점,
  파일 재열람 없음). 분류가 없는 파일은 앞 3청크만 봄.
- GeoTIFF: 오버뷰가 있을 때만 128px 흑백 썸네일(2~98 백분위 대비), 없으면 종전 범위 사각형.
- 부속 파일(.shx·.dbf·.cpg·.prj·.tfw)은 그림 대신 구분되는 값 표시 — 머리글만 읽는
  가벼운 분석기 추가.
- 값이 없는 완료 카드는 「보여 줄 값이 없음」 한 줄.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 19:17:00 +09:00
eomsangdon 82e3c3e02e Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-04 19:14:11 +09:00
eomsangdon c3c992c28b Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1 2026-09-04 19:14:10 +09:00
eomsangdon d82af25d88 Merge remote-tracking branch 'origin/main_desktop_1' into main_laptop_1 2026-09-04 19:14:09 +09:00
eomsangdonandClaude Opus 5 5203191751 feat(B05): 진입 로딩 3D 후순위 · 도넛 회전 분리 · 직교/원근 전환 단추
사용자 지시(2026-09-04) — 「3D 는 보조라 후순위. 조용히 뒤에서 뜨고 다 되면
팝업으로 알릴 것. 로딩 도넛은 진행률과 무관하게 계속 돌고 숫자만 갱신」,
그리고 「직교/원근 전환 단추는 넣음」.

진입 로딩
- 단계 5 → 4. ④ 종·횡단 자료까지 끝나면 로딩 표시를 걷어 화면을 바로 쓰게 함.
- 3D 지형은 finally 뒤 배경 작업으로 돌리고 끝나면 토스트 「3D 지형 준비 완료」.
  실패해도 나머지 화면은 그대로 씀. [초기화]는 재진입이라 같은 규칙이 걸림.
- 「확정 지표면 없음」 판정을 ③ 뒤로 앞당김.

진행 서클(공용)
- 회전 껍데기(`__spin`)를 한 겹 두고 CSS 회전을 거기에만 검. 껍데기는 늘 돌고
  안쪽 svg 는 12시 고정이라 호·숫자가 제자리에서 갱신됨. `is-indeterminate` 제거.

직교/원근 전환
- 카메라 두 벌을 `B05_Profile_UI_Viewer_Camera.ts` 가 쥐고 갈아 끼움. 위치·시선·
  근평면·먼평면과 보이는 크기를 옮겨 화면이 튀지 않음.
- 마커 입력·구조물 클릭·커서 피벗 유틸이 카메라를 함수로 받게 바꿈(공용 유틸은
  값도 그대로 받아 B04 호출부는 무변경).

자체검증(공용 브라우저 5173, 용화_LAS) — 진행률 25→50→75%, 회전 애니메이션
running 유지(변환행렬 표본 전부 다름), 로딩 걷힌 순간 그래프선 2·좌측단추 61·
3D 메쉬 2, 1.8초 뒤 메쉬 489 + 토스트. 초기화 재진입도 같음. 전환 단추는
Ortho ↔ Perspective 왕복 후 값 완전 일치, B04 페이지 오류 0건.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 19:13:46 +09:00
eomsangdonandClaude Opus 5 02bd45bf3c feat(B07): 표준 횡단면도 — 치수·측구 확대도·암반선 2단
빈 도각이던 표준 횡단면도를 실제 도면으로 만듦.

- 본 그림: B06 좌측 패널 모식도와 같은 배치를 실치수로 그림(1/50). 노폭·노견·
  측구 상단폭·노면 전폭에 치수선(눈금+치수값)을 붙이고, 절토·성토 경사비와
  횡단경사를 표기. 값은 B06 표준 횡단면 설정을 그대로 읽고 없으면 config 기본값.
- 측구 부분 확대도(1/10): 상단폭·저폭·깊이 치수 + 확대 축척 표기.
- 암반 2단 절토: 아래는 암반 경사(1:0.4), 위는 토사 경사(1:1), 갈리는 높이에
  암반선을 파선으로 긋고 각도를 함께 적음.
- 암 L형 측구·포장 횡단경사는 주기(※)로 적음 — 본 그림은 토사 기준.

곁들여: Router_Support 가 700줄을 넘어 재수출 import 를 한 문장으로 합쳐 681줄로
줄임(기능 변화 없음).

검증: 치수값이 STANDARD_CROSS_SECTION 과 일치(500·3000·500·900·4000), 확대도
900·300·300 + S=1/10, 2단 절토선 꺾임점 1개, 콘텐츠 358.2x172.6 mm ≤ A1 작도영역.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 19:09:06 +09:00
eomsangdonandClaude Opus 5 4e4bfa2354 feat(B02/B03/B04/B05): 계획노선 사용 범위 · 절단 여유 3m · 배수유역도 줌·측점 표기
- 계획노선 사용 범위: B02 등록에 시작·종료 누가거리 두 칸 추가, B01 수정 모달에서도
  변경. projects.route_start_m·route_end_m 신설(015_route_range.sql).
  load_design_route 가 범위 절단 → 서피스 트림 순서로 적용. 시작 >= 종료는 화면·서버
  양쪽에서 차단. 비우면 전 구간으로 종전과 같음.
- 서피스 절단 여유 기본값 30m → 3m (SURFACE_ROUTE_EDGE_TRIM_M).
- B04 지도·B05 배수유역도 줌 상한을 「화면 폭 20m」 기준으로 계산(고정 8배·16배 폐지).
  4배를 넘으면 배경 그림 흐림 보간 해제.
- 계획선 위 측점 눈금·번호 표기(측점번호+잔여거리). 관 마커와 겹치면 반대쪽으로 밀고,
  되꺾임 구간에서 라벨이 겹치면 건너뜀. 그리기 코드는 두 화면 공용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 19:08:11 +09:00
eomsangdonandClaude Opus 5 18c174036b feat(B07): 계획평면도(라이다) — 지표면 음영기복 배경 1차 배치
빈 도각이던 라이다 계획평면도에 지표면 탑뷰 그림을 얹음.

- 확정 DTM 격자를 도곽 범위로 잘라 음영기복 PNG 를 만들고 Image 엔티티로 실음
  (북서 315도·고도 45도, 한 변 최대 1,600 px). 점구름 4,900만 점을 그대로
  그리지 않음.
- 어느 지표면을 쓸지는 1단계 확정값을 따름 — DrainageContext 에 surface_params
  를 실어 전달.
- 축척·도곽·장 나눔은 계획평면도와 같음(1/1,200) — 노선이 같은 자리에 섬.
- entities_bbox 가 꼭짓점 배열(points)을 세도록 고침. 세지 않으면 그림이 도곽
  계산에서 통째로 빠짐.

검증(용화_LAS): 콘텐츠 726.1x487.2 mm ≤ A1 작도영역, 그림 범위 안에 노선이
완전히 들어감, 음영기복 준비 0.4초·자료 214 KB. 능선·계곡이 눈으로 구분됨.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 19:00:38 +09:00
eomsangdonandClaude Opus 5 d662b0725d feat(B07): 용지도 — 등고선 배경 + 연속지적도·행정구역·범례
빈 도각이던 용지도를 실제 도면으로 만듦. 축척·도곽·장 나눔은 계획평면도와
같고(1/1,200) 배경도 같은 창구(map_background)를 씀.

- 연속지적도 필지 경계 + 지번(jibun) 표기. 작은 필지는 솎고(종이 6x3 mm 미만),
  도곽을 통째로 감싸는 임야 대필지는 도곽 중심에 지번만 적음 — 그러지 않으면
  노선이 대필지 안에 들어앉을 때 지번이 통째로 사라짐.
- 시군구·읍면동/리 경계를 파선·일점쇄선으로 구분. 지적·행정 경계는 도곽 범위로
  절취 — 자르지 않으면 산지 대필지 하나가 도면을 10 km 로 벌림(실측 8,368 mm).
- 도면용 색을 화면용과 따로 정함(등고선을 가장 옅게, 행정 경계를 굵고 진하게).
- 범례를 유역도 정보표 자리(오른쪽 칸 방위표 아래)에 넣음.
- _geometry_lines 가 MultiPolygon 을 편다 — 지적·행정구역이 그 형식임.

검증(용화_LAS): 콘텐츠 734.3x489.2 mm ≤ A1 작도영역, 지번 「산77-15임」이 지적
속성과 일치, 도면 목록에 landuse 가 실림. 이 노선은 임야 대필지 한 곳에 통째로
들어가 경계선이 도곽 안에 없음 — 자료대로의 정상 결과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:51:23 +09:00
eomsangdon cf8b254b61 Merge remote-tracking branch 'origin/sub_desktop_1' into sub_laptop_1 2026-09-04 18:44:01 +09:00
eomsangdon 978fc742d7 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-04 18:44:00 +09:00
eomsangdon 8f45b95e7c Merge remote-tracking branch 'origin/main_desktop_1' into sub_laptop_1 2026-09-04 18:43:58 +09:00
eomsangdonandClaude Opus 5 99fa98a0dd feat(B05): 초기화 범위 확대 — 배수유역 산출물까지 초기값으로 촬영·복원
- 스냅샷 대상이 B04_PreProcess/drainage/edits 에서 drainage 폴더 통째로 넓어짐
  (00_watershed_response ~ 04_detailed_basins 포함). 관을 옮겨 다시 나뉜 세부유역이
  초기화로 되돌아가지 않던 구멍을 막음
- 용량 실측: 배수유역 8.6MB, 스냅샷 전체 3.9MB → 약 12MB
- 옛 스냅샷(edits 만 촬영) 호환 — 그 경우 예전처럼 관 지점 편집분만 복원
- 3D 코리도는 제외 — 스냅샷 시점(체인 직후)에 아직 없는 값이라 별도 판단 필요

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:43:49 +09:00
eomsangdonandClaude Opus 5 6dd0dec93a feat(B07): 계획평면도 3종 — 수치등고선 배경 위 노선·측점·구조물
빈 도각이던 계획평면도(지형·노선배치도·배치도)를 실제 도면으로 만듦.

- 배경 공용화: map_background() 한 창구로 도엽 등고선·세류선 읽기·좌표 환산·
  도곽 절취를 모음. 유역도와 계획평면도가 같은 것을 부르고, 환산 결과는
  파일 mtime 을 키로 캐시(_metric_lines_cached).
- 축척 1/1,200 고정(DRAWING_SCALE_PLAN) — 지식DB 「설계제원_총괄」 측량·도면
  기준. 횡단면도와 같은 원칙으로 줄이지 않고 안 들어가면 장을 나눔(plan_chunks,
  종단 측점 기준·경계 측점 1개 중복).
- 세 장이 같은 배경·같은 도곽 배치를 쓰고 주제만 다름. 측점 눈금은 종단 측점
  좌표로 찍고, 구조물은 pipe_points.json 정본을 읽어 마름모+이름으로 표기.
- 도면 목록·단건 조회에 kind="plan" 추가. 화면 목록은 id 접두어로 묶어 장이
  나뉘어도 한 그룹으로 보임.

검증(용화_LAS): 콘텐츠 734.3x489.2 mm ≤ A1 작도영역 739.2x499.2, 노선 실거리
630.1x214.2 m → 종이 525.12x178.46 mm(실측 0.83333 mm/m = 1/1,200 일치),
배경 등고선 180줄이 세 장 동일, 유역도 8.5초 → 계획평면도 1.3초(캐시 적중,
파일 재읽기 없음).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:38:43 +09:00
eomsangdonandClaude Opus 5 4be8c19e97 feat(B06): 표준 횡단면 설정을 「표준횡단면 상세값」 한 컨테이너로 통합
- 토사/암/포장 3그룹이 같은 필드를 각각 갖던 화면을 공통 한 벌로 합침 (칸 31 → 15)
- 공통 = 노폭·노견·측구·절토/성토 경사·횡단 경사, 구분선 뒤 암 = 절토 경사·L형 측구,
  구분선 뒤 포장 = 횡단 경사
- 저장 구조는 3그룹 그대로 — 공통값은 고칠 때 세 그룹에 펼쳐 넣음
- 옛 프로젝트가 그룹마다 다른 공통값을 갖고 있으면 토사 값 기준으로 한 벌 통일
  (사용자 확정 2026-09-04)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:36:46 +09:00
eomsangdonandClaude Opus 5 84042fe010 feat(B05): 3D 절토 구간 측점 표고 · 전 측점 라벨 거리 솎기
사용자 지적(2026-09-04) — 「절토가 된 경우 측점 표시가 안 따라감」,
「측점 라벨이 5측점 기준으로만 붙음, 전체 라벨이 있으면 좋겠음」.

- 측점 막대 표고에서 `max(지반, 계획고)` 제거. 코리도가 켜져 있으면 계획고를
  그대로 씀 — 절토는 계획고가 지반보다 아래라 max 로는 막대만 원지반에 떠 있었음
  (실측 0측점 3.2m 공중).
- 라벨을 규칙 측점 전부에 만들고(`STATION_LABEL_STEP` 제거), 카메라 거리로 솎음
  (`LABEL_LOD` 150m 미만 전부 · 400m 미만 2칸 · 그 밖 5칸). 구조물·BP·EP 는 항상.
- 뷰어 렌더 루프에서 카메라~시점 거리로 단계를 갱신(단계 불변이면 즉시 반환).

자체검증(공용 브라우저 5173, 용화_LAS) — 절토 3측점·성토 10측점 모두 막대와
코리도 표면 높이차 0.80m(띄움값). 라벨 66개 중 화면맞춤 23 · 중간 39 · 근접 66.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 18:34:05 +09:00
eomsangdonandClaude Opus 5 6ff86cdd20 feat(B05): 3D 뷰어 원근 카메라 복귀
사용자 지시(2026-09-04) — 「이전 방식이 좋았음」. 2026-08-25 `f6296fa7` 에서
직교로 바꿨던 것을 시야각 45° 원근으로 되돌림.

- 직교 전용 모듈 `B05_Profile_UI_Viewer_Camera.ts` 삭제, 뷰어 4자리 복원
  (카메라 생성 · 리사이즈 종횡비 · 화면맞춤 · 투영행렬).
- 근평면 상한 0.4m 신설 — 휠 확대가 커서 아래 지점 0.5m 앞에서 멈추는데
  근평면을 맞춤거리의 1/1000 로만 두면 긴 노선에서 그 0.5m 를 넘어 지형이 잘림
  (용화_LAS 맞춤거리 1,208m → 상한 없으면 근평면 1.21m).

자체검증(공용 브라우저 5173, 용화_LAS) — 네 방향 모두 원근 45°, 확대 한계
0.516m > 근평면 0.4m 로 잘림 없음, 구조물 클릭 선택·해제 그대로.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 18:26:26 +09:00
eomsangdonandClaude Opus 5 cc15a83f67 feat(B03~B08): 제목 줄 프로젝트 이름 + 계획노선 CSV 문구 정리
- 워크플로 상태 응답에 project_name 추가. 좌측 제목 패널 같은 행 오른쪽 끝에
  프로젝트 이름 표기(길면 말줄임, 전체는 툴팁). 이름표를 오버레이 쪽에 두어
  레이아웃을 직접 조립하는 B03 까지 여섯 화면이 한 곳으로 반영됨.
- fetchProjectWorkflowState 가 {status, workflow_state} 껍데기를 벗기도록 수정.
- 계획노선을 「업로드한 CSV」로 적은 주석을 「계획노선(정본)」으로 정리.
  업로드 판정이 .csv 개수만 세어 shapefile 을 막던 것도 .shp 포함으로 맞춤.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:26:17 +09:00
eomsangdonandClaude Opus 5 b51c887549 feat(B05/B06): 종단 그래프 줌 버튼 단순화 + 세로 자동 맞춤
- 줌 버튼을 셋(줌인·줌아웃·초기화)으로 줄임. 세로 배율·창 이동 버튼 제거
- 배율 1 = 기본값이자 축소 한계 — 한계에 닿은 버튼은 흐리게 죽임
- 세로는 보이는 누가거리 구간의 지반·계획선 범위로 자동(공통 함수
  windowElevationRange, B05·B06 종단이 함께 씀). 스크롤이 멈춘 뒤 0.16초에 갱신
- 계획고 편집 버튼을 누르고 있는 동안 Y 축 고정, 손을 떼면 다시 맞춤
- 유토곡선 Y 도 같은 창 기준(전 구간 ±200㎥ 고정 해제)
- B06 종단이 쓰던 공통 Y 스케일(calculateYScale) 제거 — 횡단 카드는 원래 안 쓰던 값

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:14:59 +09:00
eomsangdonandClaude Opus 5 022908abed fix(B07): 횡단면도 칸 크기 통일 — 축척 1/100 유지
실측 결과 축척은 문제가 아니었음. 실거리 3 m 가 모든 단면에서 30.0 mm
(mm/m = 10.0 단일값). 뒤죽박죽으로 보인 원인은 칸 크기 — 한 장 안에서
폭 116~218 mm, 높이 29~178 mm 로 제각각이었음.

- 장별 완전 통일: 한 장 안 모든 칸을 그 장 최대 블록 크기로 통일(_grid_for).
  테두리는 build_cross_drawing(cell_frame=...) 로 칸에 맞춰 그림.
- 장 경계는 전체 최소 장수가 되도록 동적계획으로 선택(_sheet_breaks).
  앞에서부터 채우면 바로 뒤의 큰 단면이 칸을 키워 6칸짜리 장에 1개만
  실리는 낭비가 있었음.
- 축척은 지식DB 「설계제원_총괄」 기준 1/100 고정 — 어떤 경우에도 줄이지 않고
  안 들어가면 장을 나눔. 장수는 세트마다 달라짐(2026-09-04 사용자 확정).

검증(용화_LAS): 장 21개 전부 칸 크기 단일값, mm/m = 10 단일, 측점 65개
누락 0, 칸 밖 이탈 0.000 mm. 실서버 API 로도 동일 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:12:54 +09:00
eomsangdonandClaude Opus 5 149920f3b5 feat(B01): 모달 바깥 클릭 닫기 + 회사 로고 기본 연결
- 모달 닫기 규칙 일원화 — 바깥 클릭·Esc·[취소] 모두 같은 경로(attachModalDismiss).
  고친 게 있으면 공용 showConfirmDialog 로 한 번 묻고, 없으면 바로 닫음.
  껍데기 세 곳(Modals·AssetPicker·TempModal) 모두 적용.
- 확인창 z-index 토큰 --z-confirm(1050) 신설 — 모달(1000) 뒤에 깔리던 문제 해소.
- 프로젝트 로고 칸에 회사 기본 연결 표시 — 프로젝트 값이 비면 회사 로고를
  「회사 기본 로고 · <자산명>」으로 보이되 저장값은 계속 null(연결 유지).
  [기본으로] 로 전용 로고 해제. 남의 회사 프로젝트에는 기본을 내밀지 않음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:05:23 +09:00
eomsangdonandClaude Opus 5 92441eb8c9 fix(B05): 초기화 후 계획선이 원지반과 어긋나던 문제
브라우저 세션에 남은 계획선 편집 초안이 초기값 위에 다시 얹혀,
초기화 직후에는 멀쩡하다가 새로고침 한 번에 계획선이 측점에서 떨어짐
(실측: 측점 66개 중 25개가 10cm 초과, 최대 6.0m).

- [초기화]가 `b05-profile-alignment-draft:*` 세션 초안을 전부 제거
- 노선번호 없는 초안 키(`:none`) 폐지 — 어느 노선에나 되붙어 초기화로도
  떨어지지 않던 구멍

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 17:02:07 +09:00
eomsangdonandClaude Fable 5 738e09e7b1 feat(B05/B06): 구조물 선택 화면 전체 활성화 + 세션 상시 공유
- 바깥 선택이 폼에 실릴 때 접힌 좌측 패널 자동 펼침(onReveal 창구)
- 계곡 통과 시설 목록 행도 보이는 자리로 스크롤(rAF)
- 종단 테이블 구조물 구간 판정을 허용오차 비교로 교체 + 병합 측점 포함(강조 0건→6건)
- 구조물 선택 세션을 소비형에서 유지형으로 — B06 카드·목록·부재 선택도 같은 칸에 기록,
  양쪽 진입 시 복원(목록 지연 로드 대비 이중 복원)
- 조정창 닫기가 세션 선택까지 지우던 결함 수정(부재 해제와 측점 해제 분리)
- B05 페이지 700줄 유지 위해 상단측 세션 보관을 Page_Helpers로 이동(동작 불변)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 16:27:35 +09:00
eomsangdonandClaude Opus 5 ebef874e5f feat(B05): 부재키 일원화 — 세월교·BOX암거까지
- 이름표를 횡단도 이름으로 통일 — 세월교 유입·유출, BOX 좌·우, 본체는 body
- 조정창 하나가 여러 부재를 다루므로 `측이름:부재` 꼴 — 강조는 전체 키, 조정창은 앞칸만
- B06 갈래 태우기 신규 모듈 — 세월교/BOX/기슭막이 조정창으로 각각 보냄
- 저장 코리도 만료(BUILD_VERSION 91→92) — 재계산 비용 실측 차이 없음(5.1초 vs 4.7초)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 15:32:10 +09:00
eomsangdonandClaude Opus 5 2878ab9617 feat(B05): 3D 코리도 구조물 개별 선택
- 구조물 솔리드에 부재키(key) 부여 — B06과 같은 이름(inlet·outlet·extra{i}·bextra{i}·own·basin·pipe)
- 구조물 메쉬에 신원표(userData) — 이름 규칙은 그대로 유지
- 신규 모듈 두 개 — 3D 클릭·강조(Viewer_Structure_Pick), 좌측 패널 연결·B06 넘김(Structure_Pick_Session)
- 좌측 패널·종단에서 고른 것도 3D가 따라오도록 선택 동기화에 포트 추가
- B06 진입 시 세션 넘김값으로 그 측점 카드 선택·스크롤 + 기슭막이 조정창 열기
- 저장 코리도 만료(BUILD_VERSION 90→91) — 옛 저장본엔 부재키가 없음
- 뷰어 700줄 여유 — 검증용 요약을 Viewer_Debug로 분리(동작 불변)
- 검증 수단 — __corridorScene에 camera·project 추가

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 15:04:38 +09:00
eomsangdonandClaude Opus 5 e0e9d819c4 fix(A06): 인증 코드 단계가 새로고침에 사라지던 문제
휴대폰에서 코드를 보러 메일 앱에 다녀오면 브라우저가 탭을 버려 페이지가 다시 뜨는데,
단계 상태가 메모리에만 있어 이메일/비밀번호 화면으로 되돌아갔다(2026-09-04 사용자 보고).
받은 코드가 무용지물이 되고, 다시 로그인하면 새 코드가 발급돼 앞 코드는 폐기됐다.

- 코드를 보낸 시점에 이메일과 발송 시각만 sessionStorage 에 남기고(비밀번호는 저장 안 함),
  화면이 다시 뜨면 그 단계로 되돌린다 — 이메일 채움·잠금, 코드 입력칸 노출, 재발송 대기도
  발송 시각 기준으로 이어 센다.
- 서버 유효시간(5분)이 지난 값은 되살리지 않고 지운다. [이메일/비밀번호 다시 입력]·로그인
  성공 시에도 지운다. 세션 저장이 막힌 브라우저면 예전처럼 메모리로만 동작한다.
- 서버는 손대지 않았다 — 코드는 이미 DB 에 5분간 있고 검증에 이메일+코드만 쓴다.

검증(공용 브라우저 5174): 대기 상태를 심고 새로고침 → 코드 입력칸 노출·이메일 잠금 채움·
버튼 "인증하고 로그인"·"43초 후 재발송" 복원. [이메일/비밀번호 다시 입력] 누르면 대기 상태
삭제되고 비밀번호 화면 복귀. 발송 6분 전 값은 복원하지 않고 삭제.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 12:56:50 +09:00
eomsangdonandClaude Opus 5 5df79d2dff fix(B05): 저장 전 구조물 [삭제] 무동작 — 조각이 옛 배열을 고치던 문제
증상: 방금 [추가]한(아직 저장 전) 항목을 목록에서 고른 뒤 [삭제]를 눌러도 목록에
그대로 남음. 저장 뒤에는 정상.

원인: 700줄 분리(2026-09-04)로 `_Commit`·`_Events` 조각이 `structures` 배열을
**참조로** 받아 두는데, `setStructures()` 가 `structures = next.map(...)` 로 배열을
갈아끼움. 화면 목록은 새 배열로 그려지고 조각은 옛 배열을 splice 해 사라지지 않았음.
같은 이유로 `renderOptionFields()` 의 `optionInputs = []` 도 조각이 옛 칸 목록을
읽을 수 있어 저장값이 어긋날 자리였음.

조치: 두 배열 모두 **자체를 바꾸지 않고 내용만 갈아끼움**(`splice` · `length = 0`)하고
`const` 로 못박아 다시 갈아끼우지 못하게 함. 데이터 흐름은 그대로 — 삭제는 캐시만
바꾸고, 영구저장은 [저장]에서만 나감(5장 데이터 3층).

검증(시험 프로젝트 8cd2e635, 공용 브라우저 실조작):
- 저장 전: [추가] → 목록 12건 → 고르고 [삭제] → **11건으로 사라짐**, 서버는
  revision 4·0건 그대로(쓰기 없음), 세션 캐시도 빈 목록
- 저장 경로: [추가] → [저장] revision 5·1건 → [삭제](서버 5·1건 유지) → [저장]
  revision 6·0건
- 저장값: 옹벽 옵션 `height_m 2.5 · length_m 10 · before_m 5 · after_m 5`,
  기준 5+0(100m) → 구간 95~105m 정상
- 시험 프로젝트는 구조물 0건으로 되돌려 둠. `tsc --noEmit` 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 12:50:17 +09:00
eomsangdon 02fb075916 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-04 11:38:36 +09:00
eomsangdonandClaude Opus 5 7500f37acd refactor(B05): 구조물 배치 패널 700줄 분리 (동작 불변)
B05_Profile_UI_Structures_Panel.ts 1024 -> 669줄. 조각 셋 모두 700줄 이하.
- _Structures_Panel_Types.ts(94줄) — GROUP_LABELS · PipeFacilityItem · StructuresSection ·
  StructuresCallbacks · 기본 구간 길이 상수. 패널이 다시 export 해 옛 임포트 경로 유지.
- _Structures_Panel_Commit.ts(312줄) — 폼 불러오기(구조물·계곡 통과 시설)와 [추가]/[수정]
  저장, 목록 알림. 검증 순서·분기·수치는 그대로이고 패널 클로저 값만 ctx 로 받음.
- _Structures_Panel_Events.ts(146줄) — 드롭다운·버튼·측점 입력 배선. 리스너 본문 그대로.
  StructuresEventContext 가 StructuresCommitContext 를 상속해 패널은 컨텍스트 하나만 만듦.
  가변 상태 4개(editingId · selectedPipeChainage · tempPipeChainage · positionConfirmed)의
  주인은 그대로 패널이고, 읽기 함수 + set* 로만 넘김.

검증: 분리 전 export 4개가 진입 파일에서 그대로 보임, tsc --noEmit 통과, prettier 적용,
tmp/tests 183 passed. 공용 브라우저(5174)에서 실제 조작으로 저장 경로까지 확인 —
C 사면안정/옹벽을 기준측점 5+0 에 [추가] -> 목록 반영, [저장] -> 서버 정본 2건(revision 0->1),
새로고침 후 목록에서 골라 [삭제] -> [저장] -> 서버 0건(revision 4). 시험 프로젝트는 원래
상태(구조물 0건)로 되돌려 둠.

참고(분리와 무관한 기존 동작): 방금 [추가]해 아직 저장 전인 항목은 structure_id 가 없어
[삭제]가 듣지 않음. 저장 뒤에는 정상 삭제됨. 분리 전 코드와 같은 분기라 이번 변경과 무관.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 11:37:46 +09:00
eomsangdon 0c02a8876f Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-04 11:34:14 +09:00
eomsangdonandClaude Opus 5 907cfc144b refactor(B07): 상세 설계 화면 700줄 초과 분리 — 좌측 패널
732줄 한 파일을 둘로 나눔 (동작 불변, 순수 이동).
- `B07_DesignDetail_UI_Page.ts` 517줄 — CAD 임베드·postMessage 교신·저장/확정 흐름
- `B07_DesignDetail_UI_Panels.ts` 234줄 — 도면 목록 12분류(`DRAWING_GROUPS`)와
  측점 설계 정보 카드. DOM 만 만들고 고르기 동작은 콜백으로 받음

검증: 공용 브라우저 B07 화면 실측 — 도면 목록 패널 1개·항목 54개(「설계 도면 49건」),
CAD iframe 1개 로드, 콘솔 오류 0. `tsc --noEmit` 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 11:33:51 +09:00
eomsangdonandClaude Opus 5 887444fe7c refactor(common_util): 횡단 설계 계산 700줄 초과 분리 — 단면 기하
728줄 한 파일을 둘로 나눔 (계산 불변, 순수 이동).
- `common_util_cross_design.ts` 405줄 — 표준단면 해석·설계선 조립·면적 산출
- `common_util_cross_design_geometry.ts` 333줄 — `SectionGeometry`(노면·측구·사면 설계고)
  와 그것만 쓰는 상수(행진 간격·최대 거리)·`sideRole`·`ResolvedGroup`

파이썬 짝 파일(`B06_Section_Engine_Design.py`)은 손대지 않음 — 미러 관계 유지.

검증: 공용 브라우저 B06 횡단 카드 실측과 서버 계산값 대조 — 측점 0에서
절토 화면 2.50㎡ / 서버 2.4985㎡, 성토 화면 1.85㎡ / 서버 1.8521㎡ 로 일치
(카드 273장 렌더, 콘솔 오류 0). `tsc --noEmit` 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 11:30:22 +09:00
eomsangdonandClaude Opus 5 f541ab4480 refactor(B03): 파일 입력 화면 700줄 초과 분리 — 판정 규칙·업로드 흐름
925줄 한 파일을 셋으로 나눔 (동작 불변).
- `B03_FileInput_UI_Page.ts` 692줄 — 화면 조립·카드 렌더·현황 표시
- `B03_FileInput_UI_Page_Flow.ts` 262줄 — 중단 세션 이어올리기·보관함 이관·청크 업로드·
  초기 계산 중 재잠금·서비스워커. 화면 상태는 `UploadFlowContext` 창구로만 받음
- `B03_FileInput_UI_Page_Rules.ts` 107줄 — 필수 카드 판정·파일 적합성·업로드 가능 판정·
  서버 파일의 카드 매핑 (상태를 가두지 않는 순수 함수)

검증: 공용 브라우저에서 새 프로젝트 만들어 노선 5종 실제 선택·업로드 —
카드 5장 선택 후 [파일 업로드] 비활성, [LAS 없이 설계] 켜면 활성(판정 규칙 정상),
업로드 후 4장 완료 표시(20초). 남은 `route_prj` 오류·400 은 지형 자료가 없어
분리 전에도 같던 것. `tsc --noEmit` 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 11:19:04 +09:00
eomsangdonandClaude Opus 5 41cfab22e9 refactor(ui_template): 공통 컴포넌트 700줄 초과 분리 — 차트·기본 스타일
917줄 한 파일을 넷으로 나눔 (동작 불변, 순수 이동).
- `ui_template_elements.ts` 363줄 — 버튼·입력·선택·카드·태그·토스트·확인창·셸
- `ui_template_elements_styles.ts` 369줄 — `injectBaseStyles()` 와 규칙 문자열
- `ui_template_elements_chart.ts` 185줄 — 공통 라인 차트
- `ui_template_elements_base.ts` 33줄 — 요소 생성 헬퍼 `el` (셋이 함께 써 순환 방지)

본체가 `export *` 로 다시 내보내 호출부의 import 경로는 불변.

검증: 분리 전 `export` 25개 전부 유지(+`el` 공개 1개 추가), `tsc --noEmit` 통과,
tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 11:19:04 +09:00
eomsangdonandClaude Opus 5 37fc63ba4c refactor(B04): 지표면 3D 뷰어 700줄 분리 (동작 불변)
B04_PreProcess_UI_TerrainViewer.ts 958 -> 698줄.
- _TerrainViewer_Chrome.ts(209줄) — 상태줄·표시 토글·등고선 간격 폼·뷰포트·축척·컴파스·
  표고 범례·로딩 서클 DOM 생성. 만드는 순서·클래스·인라인 스타일 그대로.
- _TerrainViewer_Contours.ts(189줄) — 등고선 적재(fetch·라인 생성·라벨·범례). 뷰어
  클로저가 쥐던 값만 ctx 로 받고 본문 수치·로직은 그대로. 호출부는 얇은 래퍼로 유지.

검증: export 2개(SurfaceTerrainViewer · createSurfaceTerrainViewer) 분리 전과 동일,
tsc --noEmit 통과, prettier 적용. 공용 브라우저(5174) B04 실조작 — 뷰어 2개·캔버스 2개
렌더, 상태줄 CLASSIFICATION · DTM · 등고선 1m, 등고 라벨 40개(상한), 범례 940m/760m,
등고선 토글을 끄면 라벨 0 → 켜면 40 복귀, 간격 5m 적용 시 상태가 5m 로 바뀌고
1m 로 되돌리면 원상 복귀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 11:18:22 +09:00
eomsangdonandClaude Opus 5 9c141ac717 refactor(B05): 종단 패널 콜백 타입 분리 (726 -> 688줄)
RouteProfilePanelCallbacks 인터페이스만 B05_Profile_UI_Profile_Panel_Types.ts(52줄)로
그대로 옮기고, 진입 파일에서 다시 export 해 옛 임포트 경로(_Profile_Render 등)를 유지.
런타임 코드는 한 줄도 건드리지 않음.

검증: 분리 전 export 2개(RouteProfilePanelCallbacks · createRouteProfilePanel)가 진입
파일에서 그대로 보임, tsc --noEmit 통과, prettier 유지. 공용 브라우저(5174) 실조작 —
종단 패널 측점 66개·표 12행·줌 버튼 7개·유역 11개 정상, 측점 선택 후 방향키 862.99->863.09m,
Ctrl+Z 로 862.99m 복귀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 11:12:05 +09:00
eomsangdonandClaude Opus 5 fbbedc7230 fix(700줄분리): 진입 파일에서 분리 전 이름 전부 재노출
laptop-sub 가 쓴 AST 대조법(분리 전 커밋의 모듈 정의 이름 ↔ 지금 모듈 `dir()`)을
내 4개에도 적용해 누락 26개를 찾아 되살림.

- `B03_FileInput_Router.py` 누락 17 → 0 (헬퍼 13 + 청크 엔드포인트 4)
- `B03_FileInput_Router_Temp.py` 누락 5 → 0 (`_total_chunks` + 청크 엔드포인트 4)
- `B07_DesignDetail_Router_Support.py` 누락 3 → 0 (`_BASIN_MAX_DISTANCE_M` ·
  `_clip_segment` · `_geometry_lines`)
- `config/config_system.py` 누락 1 → 0 (`_SCS_SOIL` — `import *` 가 밑줄 이름을 안 실음)

검증: 네 파일 모두 **정의 이름 누락 0**(28·19·36·246개 대조), 라우트 수 유지
(B03 7 · temp 9+attach 1 · B07 7), ruff check 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:53:37 +09:00
eomsangdonandClaude Opus 5 322a2f1f13 fix(B04): 분리로 사라진 이름을 진입 라우터에서 재노출
700줄 분리 때 _Router_Progress · _Router_Status 로 옮겨 진입 파일에서 안 보이게 된
PROGRESS_FILE_RELATIVE · _progress_file_path · read_surface_progress ·
get_wf1_analysis_status · get_surface_model_preview 를 as 재노출로 되살림.
옛 경로를 보던 호출부·테스트가 그대로 동작하게 하려는 것(laptop-main 의 B07 사례와 같은 사고 예방).

검증: 분리 전 모듈 정의 이름을 전수 대조해 누락 0개(_Router 16개 · _Router_Watershed 30개),
라우트 9개 유지, ruff check/format 통과, tmp/tests 183 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:51:39 +09:00
eomsangdonandClaude Opus 5 0b16a1240c fix(B07): 분리한 유역도 이름을 진입 파일에서 재노출
`_Support_Basin.py` 로 옮긴 뒤 `CONTOUR_FILE` · `STREAM_FILE` · `_basins_crs` ·
`_geojson_payload` · `_geojson_features` · `_too_far_from_route` 가 진입 파일에서
사라져, 옛 이름을 참조하던 다른 워크트리의 테스트가 ImportError·AttributeError 로
깨졌음(laptop-sub 보고 2건). 프로덕션 호출부는 새 모듈을 직접 봐 동작에는 영향 없었음.

`as` 형태로 다시 내보내 분리 전 이름 14개가 진입 파일에서 모두 보이게 함.

검증: 진입 파일 속성 14개 전부 확인, 라우트 7개 유지, ruff check 통과,
tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:49:06 +09:00
eomsangdon cea803b281 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-04 10:34:30 +09:00
eomsangdonandClaude Opus 5 472a6f7849 refactor(config): 시스템 설정 700줄 초과 분리 — 지형·설계 파라미터
937줄 한 파일을 셋으로 나눔 (값·이름 불변).
- `config_system.py` 255줄 — 서버·로깅·DB·업로드·저장소·인증·CORS
- `config_system_terrain.py` 186줄 — 5장·5-2장 지형 분석·지표면 모델 파라미터
- `config_system_design.py` 520줄 — 5-3장~5-6장 경로·유역·종횡단·계획선 파라미터

본체가 두 조각을 `import *` 로 다시 내보내므로 기존
`from config.config_system import …` 호출부는 그대로임.

검증: 분리 전 원본과 대조해 **공개 상수 243개 전부 값 동일**(사라진 것은 설계 모듈
내부에서만 쓰는 `_SCS_SOIL` 하나 — 밑줄 시작이라 원래도 외부 참조 없음),
백엔드 재시작 후 기동 정상·주요 API 6종 200, ruff check 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:34:18 +09:00
eomsangdonandClaude Opus 5 8be60007e7 refactor(B07): 도면 조립 지원 700줄 초과 분리 — 유역도·입출력
808줄 한 파일을 셋으로 나눔 (동작 불변, 순수 이동).
- `B07_DesignDetail_Router_Support.py` 532줄 — 도면 목록·장 배치·확정 저장·재계산
- `B07_DesignDetail_Router_Support_Basin.py` 279줄 — 유역도 배경(GeoJSON·좌표 환산·도곽 클리핑)
- `B07_DesignDetail_Router_Support_Io.py` 88줄 — 원본 JSON·매니페스트 입출력, 측점 대응표

떼어낸 `watershed_source`·`clip_line_to_box` 는 본체에서 `as` 로 다시 내보내
호출부(`B07_DesignDetail_Router.py`)의 import 경로가 그대로임.

검증: 라우트 7개 그대로, 공용 브라우저 `design-drawings` 200 · `frame-template` 200,
ruff check 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:34:18 +09:00
eomsangdonandClaude Opus 5 3ec6527cc2 refactor(B03): 임시 보관함 라우터 700줄 초과 분리 — 청크·보조
749줄 한 파일을 셋으로 나눔 (동작 불변, 순수 이동).
- `B03_FileInput_Router_Temp.py` 478줄 — 묶음 생성·목록·삭제·파일 업로드·프로젝트 연결
- `B03_FileInput_Router_Temp_Chunks.py` 294줄 — 세션·조각·진행·마무리 (prefix 없는
  APIRouter 를 본체가 `include_router` 로 붙여 `/api/temp-uploads/...` 경로 불변)
- `B03_FileInput_Router_Temp_Support.py` 46줄 — 조각 수·묶음 폴더·상태 갱신 (순환 방지)

검증: 라우트 10개(temp 9 + attach 1) 경로·메서드 동일, 공용 브라우저에서 실제 청크 흐름
create 200 → upload-sessions 200 → chunks 200 → finalize 200 → delete 200,
ruff check 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:34:01 +09:00
eomsangdonandClaude Opus 5 07e6441d7f refactor(B04): 배수유역 라우터 700줄 분리 (동작 불변)
B04_PreProcess_Router_Watershed.py 714줄 -> 518줄. 산출물 기록(.npz·스테이지 기하)과
응답 좌표 변환 헬퍼 9개를 B04_PreProcess_Router_Watershed_Output.py(225줄)로 그대로 옮김.
공개 심볼 router · get_primary_region · CONTOUR_FILE · STREAM_FILE 유지
(main.py · B03_FileInput_Service_Chain · B07_DesignDetail_Router_Support 참조).

검증: ruff check 전체 통과·format 적용, tmp/tests 183 passed, 백엔드 재시작 후
경로 2개 유지, /drainage/rainfall 200, /drainage/primary-region 200(schema_version 3),
B05 화면 유역 11개·측점 66개 정상, 옮긴 순수 헬퍼 6개 직접 호출 스모크 통과.
분석 재실행(쓰기 경로)은 작업본 산출물을 덮어써서 돌리지 않음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:17:34 +09:00
eomsangdonandClaude Opus 5 c4aac6f299 refactor(B03): 파일 입력 라우터 700줄 초과 분리 — 보조·청크 업로드
872줄 한 파일을 셋으로 나눔 (동작 불변, 순수 분리).
- `B03_FileInput_Router.py` 309줄 — 파일 업로드·현황·워크플로 엔드포인트
- `B03_FileInput_Router_Chunks.py` 390줄 — 세션 생성·조각 전송·마무리·진행 조회
  (자체 APIRouter 를 본체가 `include_router` 로 붙여 경로 문자열 불변)
- `B03_FileInput_Router_Helpers.py` 268줄 — 필수 파일 판정·중복 지문 판별·단계 기록·알림

검증: 라우트 7개 경로·메서드 동일(`GET upload-overview|upload-status|workflow-state`,
`POST files|upload-sessions|chunks|finalize`), 공용 브라우저에서 노선 5종 실제 업로드 성공
(shp metadata preview_path 포함), ruff check 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:15:19 +09:00
eomsangdonandClaude Opus 5 03a193af70 refactor(B04): 지표면 라우터 700줄 분리 (경로·응답 불변)
B04_PreProcess_Router.py 718줄 -> 493줄.
- B04_PreProcess_Router_Status.py(221줄) — 분석 상태 조회·모델 프리뷰 엔드포인트.
  prefix 없이 만들고 본체가 include_router 로 실어 붙임(본체 prefix 를 그대로 상속).
- B04_PreProcess_Router_Progress.py(47줄) — progress.json 읽기·쓰기 헬퍼.
  본체와 상태 라우터가 함께 써야 해 순환 임포트를 피하려고 별도 모듈로 둠.
  본체는 write_surface_progress 를 계속 재노출(B03_FileInput_Service_WF1 이 참조).

검증: ruff check/format 통과. 라우터 경로 9개가 분리 전과 동일(중복 prefix 없음 확인).
백엔드 재시작(8001) 후 openapi 에 surface 경로 12개 그대로, 브라우저에서
/surface/status 200(진행률 100·모델 3), /surface/models 200,
/surface/models/415/preview 200 model/gltf-binary 7,946,756바이트.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:13:36 +09:00
eomsangdonandClaude Opus 5 2975ea09c7 refactor(B04): 전처리 스타일시트 700줄 분리 (동작 불변)
B04_PreProcess_UI_Style.css 870줄 -> 565줄. 하단 2D 지도·도구줄·시트 툴바 규칙을
B04_PreProcess_UI_Style_Map.css(312줄)로 값·순서 그대로 옮김. 진입 TS 에서 본체 다음에
불러 캐스케이드 순서 유지.

검증: 두 파일을 다시 이으면 원본과 **경계 빈 줄 1개 외 완전 동일**(텍스트 대조).
tsc --noEmit 통과. 공용 브라우저(5174) B04 화면 실측 — 좌측 폼 flex/gap 16px,
.b04-map flex column gap 12px(896x828), 헤더·컨트롤 정상, 3D 서피스·2D 지도 렌더 정상.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:09:10 +09:00
eomsangdonandClaude Opus 5 370ff30738 refactor(B05): 종단 스타일시트 700줄 분리 (동작 불변)
B05_Profile_UI_Style.css 1379줄 -> 592줄. 규칙을 값·순서 그대로 옮겨 두 조각으로 나눔.
- B05_Profile_UI_Style_Table.css (515줄) — 도면 테이블·값 열 오버레이·우클릭 메뉴·
  계획고 편집 버튼·줌 조작구
- B05_Profile_UI_Style_Drainage.css (289줄) — 배수유역도 패널
진입 TS 두 곳(B05_Profile_UI_Page.ts, A00_Common/b_structures_section.ts)에서
본체 다음에 순서대로 불러 캐스케이드 순서 유지.

검증: tsc --noEmit 통과, prettier 서식 유지, 공용 브라우저(5174)에서 계산 스타일 확인
(표 이름표 sticky 78px, 구배 블록 absolute, 편집 버튼 21x17, 줌 22x22, 배수유역 flex).
종단 패널 캡처가 분리 전과 **바이트 단위로 동일**(md5 ee6bc8b52adb).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:07:17 +09:00
eomsangdonandClaude Opus 5 3b89ba4fac fix(B05): 종단표 구배 3행 상시 표기
구배 L·H·S 는 값 열을 접는 규칙에서 제외 — 구조물로 좁게 갈라진 구간도 값을 적음
(2026-09-04 사용자 확정, 「각도 행」 = 구배 3행). 그 구간만 고를 때까지 값을 감추던
옛 규칙(2026-08-03)을 걷어내고, 구조물 구간 표시·하이라이트·툴팁은 유지.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 09:31:27 +09:00
eomsangdon 997e94dddb Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-04 07:31:20 +09:00
eomsangdonandClaude Opus 5 782139732b fix(B05): 종단표 구조물 열 상시 표기·직선화 자리 곡선값 제거
- 구조물 측점 판정을 문자열 키에서 누가거리 허용 오차 0.1m 비교로 바꿈 —
  구조물 목록(85.59)과 계획선 측점(85.595)이 셋째 자리에서 어긋나 구조물 열을
  못 알아보고 값을 그대로 펼쳐 이웃 값과 겹쳐 보였음. 이제 고른 때만 값 열 오버레이로 보임
- 대수차 1e-4% 미만이면 직선으로 보고 곡선 L·R 을 빈 칸으로 둠 — 직선화하면
  대수차가 0 이 되어 R = L ÷ |대수차| 가 수억 m 로 발산했음 (표·값 열 오버레이 공통)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 07:31:08 +09:00
eomsangdon ec85e42b3e Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-04 07:24:03 +09:00
eomsangdonandClaude Opus 5 2d49c59342 feat(B05): 종단도 줌·Y 표시 표고창 조작구
- B05_Profile_UI_Profile_Zoom.ts 신규 — 배율 상태(가로 폭 배수·세로 배율·창 중심)와
  버튼 7개(+ − ⇕+ ⇕− ▲ ▼ ⤢). 자리는 요약줄 오른쪽 끝(그래프 우측 상단),
  설명은 툴팁, 클릭은 stopPropagation 으로 측점 선택에 번지지 않게 함
- X 줌은 폭 배수 — computeProfileLayout 에 zoomX 를 넘겨 측점 간격·폭맞춤 폭에 함께 곱함.
  그래프·테이블·편집 버튼층·구조물 레인이 같은 매핑을 쓰므로 넷의 x 정렬이 유지됨
- Y 는 표시 표고창(제안 A) — createLongitudinalProfile 에 elevationOffsetRatio 인자 추가,
  창 높이 = 전범위 ÷ 배율, ▲▼ 가 창 높이의 10%씩 중심 이동. 세로 스크롤 없음
- 지반선·계획선·절성토 음영을 플롯 사각형 clipPath 안으로 옮김 — 배율을 올려도
  선이 축·측점 라벨 위로 흘러나오지 않음 (B06 은 배율 1·오프셋 0 이라 표시 불변)
- 요약줄 렌더러에 trailing 자리 추가, .b05-profile__zoom* CSS 추가

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 07:23:34 +09:00
eomsangdonandClaude Opus 5 ecb3bcb9ce feat(B05): 종단도 방향키 연속 이동·이력 한 덩어리·쉬프트 상하
- 방향키 청취를 패널 루트에서 window 로 옮김 — 그래프 클릭 뒤 포커스가 body 에
  남아 방향키가 아무 반응이 없던 결함 수정 (패널이 화면에 없으면 무시)
- 길게 누르면 편집 버튼과 같은 속도로 연속 이동 (0.5초 뒤 0.1초 간격, OS 키 반복 무시)
- 이력에 pause()/resume() 추가 — 연속 이동 전체가 되돌리기 1회로 원위치.
  구조물 이동은 서버 재계산이 뒤따르므로 키를 뗀 뒤 700ms 뒤 기록
- pause() 에서도 관 목록을 세션에 흘려 되돌리기 기준을 조작 전 값으로 고정
- 좌우 연속 이동 중에는 처음 잡은 구조물 측점·자체 누가거리 카운터 사용 —
  관 측점 id 가 누가거리 기반이라 한 번 옮기면 선택이 풀려 한 칸만 움직이던 결함 수정
- ProfileTools.nudge(delta) 노출 — [쉬프트] 모드의 상·하 방향키가 도구 ▲▼ 와
  같은 경로(onShift)로 구간 평행이동

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 07:11:41 +09:00
eomsangdonandClaude Opus 5 98db2731b9 style(B03): 미리보기 범위 사각형 옅은 점선 처리
어두운 테마에서 채움 35%가 회색 덩어리로 보여 범위인지 안 읽힘 — 채움 12% +
점선 테두리로 「범위」임이 드러나게 함(실측 스크린샷 확인).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 07:06:32 +09:00
eomsangdonandClaude Opus 5 ab16361125 feat(B03): 업로드 자료 카드 그래픽 미리보기 — 범위 사각형·노선 형상·범위 대조
- 계획노선 shapefile 분석에 `preview_path`(200점 안팎 솎은 좌표열) 추가 — 이미
  메모리에 있는 정점을 쓰므로 파일 재열람 0회(솎기 0.136ms)
- LAS·GeoTIFF 는 사용자 확정대로 **bbox 사각형만** (점구름·래스터 렌더 안 함)
- `upload-overview` 응답에 분석 `metadata` 동봉 — 재접속해도 같은 그림
- 카드에 SVG 미리보기 렌더(신규 `B03_FileInput_UI_Preview.ts`), 값 없으면 미표시
- 계획노선 카드는 지형 자료 범위와 대조 — 벗어나면 경고색·안내, 좌표계가 다르면
  대조 생략(재투영 안 함). shapefile 은 좌표계가 없어 같은 세트 `.prj` 값을 씀

검증: 공용 브라우저 실측(노선 선 121점·범위 안/밖·좌표계 상이 3갈래),
tmp/tests/test_b03_preview_path.py 2건, tsc --noEmit 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 07:04:33 +09:00
eomsangdonandClaude Opus 5 af85dea714 feat(ui_template): 진행단계 패널 끌어 옮기기·열림 방향 자동 전환
- 헤더를 잡아 끌어 자리 이동, 이동 범위는 상단 헤더 아래 화면 안으로 clamp
- 아래 공간 부족 시 `is-flip-up` 으로 헤더 위로 펼침(바닥 기준 정렬)
- 자리는 sessionStorage 세션 캐시에만 보관, 접힘/펴짐 뒤 방향 재판정
- threshold 4px 초과 시에만 포인터 캡처 — pointerdown 캡처는 click 대상을
  제목에서 헤더로 바꿔 접기/펴기 토글을 죽임(실측)
- 이동은 window 에서 청취 — B07 CAD iframe 위로 커서가 나가도 안 끊김

검증: 공용 브라우저 B03·B05·B07 실측(이동 오차 1px 이내, clamp·뒤집힘·토글 정상),
tsc --noEmit 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 06:37:57 +09:00
eomsangdon d1accfea0d Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-04 05:29:27 +09:00
eomsangdon 7c62896c14 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-03 21:10:11 +09:00
eomsangdon ee5fa16f6a Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-03 21:10:10 +09:00
eomsangdonandClaude Opus 5 db91109b5a fix(B03): 입력 버튼 3칸 1:1:1·높이 통일, 토글 아래 빈 자리 제거
사용자 지시(2026-09-03).

- 고르는 자리를 flex 에서 **격자 3칸**으로 바꿈. flex 로는 내용 폭이 배분에 섞여
  288·254·288 로 어긋났고 버튼 높이도 달랐다. 격자는 칸을 먼저 나누므로 1:1:1 이고,
  칸을 꽉 채우게 해 세 칸 높이가 같아진다(실측 277×47 셋 다 동일).
- 빈 오류 줄이 자리를 잡아 토글 아래가 비어 보이던 것 제거 — 글자가 있을 때만 자리를
  차지한다(`:empty { display: none }`, min-height·마진 제거).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 21:06:48 +09:00
eomsangdonandClaude Opus 5 7c3a65e814 feat(B03): 선택 영역 설명 이관 · 임시 보관함 이름·테두리 정리 · 계획노선은 shapefile만
사용자 지시 4건(2026-09-03).

- 선택 영역의 설명 문구를 좌측 안내 패널로 옮기고 본문에는 「입력 파일 선택」만 남김.
- [임시 보관함에서 불러오기] → [임시 보관함]. 버튼을 감싸던 점선 테두리 삭제 —
  버튼 자신의 테두리와 이중이었음.
- 계획노선 도형 카드는 `.shp` 만 받음. CSV 는 내부 계산이 만드는 파일이라 사용자가 넣는
  자료가 아님(사용자 확인). 컨테이너의 CSV 안내 문구도 삭제.
- 이미 `.csv` 로 올라간 옛 프로젝트가 「미등록」으로 보이지 않도록 **서버 파일 매핑에만**
  `.csv → 계획노선 도형` 예외를 둠. 새로 받는 확장자와 이미 받은 것을 갈라 둔다.

좌측 안내 문구도 함께 갱신 — CSV 언급 제거, 버튼 새 이름 반영.

검증: 실측 — 선택 영역 텍스트 「입력 파일 선택」, 보관함 테두리 none·라벨 「임시 보관함」,
계획노선 안내 문구 없음, 카드 확장자 `.shp`, 옛 CSV 는 「완료 · 용화_계획노선.csv」 유지.
typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 21:02:19 +09:00
eomsangdonandClaude Opus 5 061265f605 fix(B06): 암 경계 세션 오프셋을 자릿수 때문에 통째로 놓치던 것
세션에 저장하는 쪽(`createRockBoundaryStore`)은 키를 `toFixed(2)` 로 쓰는데 브라우저
재계산은 `toFixed(3)` 문자열로 찾아 **한 건도 못 읽었음**. 서버 경로는 키를 숫자로
바꿔 비교해 문제가 없었으므로 로컬 계산으로 옮기며 생긴 어긋남임.

키를 숫자로 되돌려 0.01m 단위로 맞춤. B06 에서 암 경계선을 올리내린 값이 이제 B05
유토곡선·횡단 면적에도 그대로 반영됨.

화면 검증(5173→5174, 같은 시점): B05 첫 진입 절토 4,774.1㎥ / 성토 16,690.7㎥,
B06 도 같은 값(재계산 입력·결과 로그로 대조 — 측점 65, 샘플 65, 자연방토 25건까지 동일).
B06 을 거치기 전과 후의 값 차이(성토 16,715.5 → 16,690.7)도 사라져 **첫 진입부터 일치**함.
진단용 임시 로그는 제거함.

전체 테스트 183건 통과, `tsc --noEmit` 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 21:01:56 +09:00
eomsangdon b0fa00f335 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-03 20:57:52 +09:00
eomsangdonandClaude Opus 5 b9e6a2b6a8 feat(B03): LAS 없이 설계 토글을 입력 컨테이너로 · 켜면 지형 자료 전체 차단
사용자 지시(2026-09-03).

- 토글을 지형 컨테이너에서 **파일 입력 컨테이너**로 옮김 — 무엇을 받을지 정하는
  스위치라 고르는 자리 옆에 선다. 서식도 입혔음(공용 체크 줄 + 켜짐 강조).
- 켜면 지형 자료를 **통째로** 받지 않음: 포인트클라우드·좌표계·래스터 카드를 모두 잠그고
  컨테이너를 흐림. 필수 판정에서도 지형 슬롯 전체가 빠진다.
- 파일 거르기를 확장자가 아니라 **배정된 카드** 기준으로 바꿈 — `.prj` 는 노선·지형이 같은
  확장자라 확장자로 거르면 노선 좌표계까지 함께 떨어졌다.
- 이미 올라간 자료(재입력)는 지우지 않고 **조작만 잠금** — 로컬로 고른 파일만 내린다.

좌측 안내 패널의 글자 크기도 공용 양식에 맞춤 — 본문 `--text-body-sm`·`--color-text-body`
(B04 `b04-surface__check` 와 같음), 제목 `--text-caption`.

검증: 실측 — 토글이 입력 컨테이너 안에 있고, 켜면 지형 카드 4장이 모두 disabled +
[파일 선택] 잠김, 기존 업로드분은 「완료」·파일명 유지. 끄면 원복. 패널 글자 14px/12px.
typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:55:29 +09:00
eomsangdonandClaude Opus 5 bc7b9e3a48 fix(B06): 유토곡선 영역 누락 — 재계산 결과와 계획선 정본이 어긋나던 것
두 곳이 같은 뿌리였음. 브라우저 재계산은 **편집이 반영된 계획고**로 횡단 설계를 만드는데
`design_profiles` 는 저장분 그대로라 두 값의 기준이 갈렸음.

1. 재계산 창구가 방금 푼 계획선을 **공유 캐시에도 얹도록** 함
   (`detail.longitudinal.design_profiles`). 이게 없으면 낡음 판정이 영원히 참이 되어
   B06 유토곡선이 통째로 빈 채로 남았음(사용자 보고). `profile_alignment`(base_pvi)는
   건드리지 않음 — 편집 델타의 기준선이라 편집분을 구워 넣으면 이중 적용됨.

2. B06 진입 정합이 **미저장 세션 편집을 못 보고 건너뛰던** 것. 서버에서 갓 받은 저장분
   끼리는 늘 일치해 「낡지 않음」이 되는데, B05가 세션에 남긴 편집은 그 안에 없음.
   세션 초안이 있으면 판정을 건너뛰고 무조건 맞추도록 함 — 재계산은 브라우저 안에서
   끝나 값이 쌈.

화면 검증(5174, 같은 시점 비교):
  수정 전 — B05 절토 4,774.1㎥ / 성토 16,715.5㎥, B06 절토 4,515.0㎥ / 성토 14,857.5㎥
  수정 후 — 양쪽 모두 절토 4,774.1㎥ / 성토 16,690.7㎥ (일치)
B06 유토곡선 영역도 곡선이 다시 그려짐(문구 없음).

전체 테스트 183건 통과, `tsc --noEmit` 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:49:25 +09:00
eomsangdonandClaude Opus 5 a00c7497c1 feat(B03): 보관함 선택 요약·「업로드할 파일을 선택하세요」 경고 제거
사용자 지시 2건(2026-09-03).

- 임시 보관함 선택 요약(「선택된 보관 자료 없음」·묶음 이름·파일 수)을 없앰. 불러온 자료는
  카드가 곧바로 보여 주고, 고르기 전 상태는 [파일 업로드]가 잠긴 것으로 이미 드러남.
  쓰이지 않게 된 `.b03-file__temp-summary` 규칙과 로케일 2건도 함께 제거.
- 입력 컨테이너의 「업로드할 파일을 선택하세요」 경고를 띄우지 않음 — 아직 아무것도 고르지
  않은 상태를 붉은 글씨로 알릴 이유가 없음. 확장자·개수·필수 슬롯 같은 나머지 사유와
  [파일 업로드]를 눌렀을 때의 검사 결과는 그대로 표시.

검증: 실측 — 보관함 자리에 버튼 하나만 남음(요약 span 없음), 오류 줄 빈 문자열,
한 줄 배치 선택 518px · 보관함 210px · 업로드 102px. typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:47:02 +09:00
eomsangdonandClaude Opus 5 d9fcfa3b96 refactor(B03): 좌측 안내 패널을 B04~B07 공용양식으로 맞춤
처음엔 B03 전용 스타일로 문단을 만들었으나, 좌측 패널 양식은 다른 단계와 같아야 한다는
사용자 지시(2026-09-03). 공용 조립으로 교체함.

- 패널 루트 `b03-file__form`, 문단은 `ui-collapsible ui-sidebar-section`,
  제목은 `ui-collapsible__title` — 외곽선·캐럿·접힘 동작이 전부 공용 CSS·동작이다.
- 값도 `b04-surface__form`·`__group`·`__group-legend` 와 같게 맞춤
  (gap·padding 16, 배경 `--color-surface-raised`, legend `--text-caption`).

검증: 실측 — 문단 클래스 `b03-file__guide-group ui-collapsible ui-sidebar-section`,
패딩 16px, 배경 `rgb(37,31,56)`, 제목 클릭 시 `is-collapsed` 토글·복귀 확인.
typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:42:50 +09:00
eomsangdonandClaude Opus 5 66717162c5 feat(B05): 도구줄 [초기화] 추가 + 쉬프트 선택 구간 빨간 강조
1. [초기화] — [↶] 왼쪽에 둠. 전체 초기화가 아니라 **마지막 [저장] 시점**이 기준점임
   (사용자 지시). 편집 스토어가 저장 시점 편집분(`savedBaseline`)을 들고 있다가 그리로
   되돌림 — 저장한 작업은 남고 그 뒤 편집만 버림. 이력에 기록해 [↶]로 되살릴 수 있음.
   되돌릴 것이 없으면 버튼은 비활성.
   자동 선형까지 지우는 `resetAll`(좌측 [초기화])과는 다른 조작임.

2. [쉬프트]로 고른 직선이 화면에 표시되지 않아 무엇이 잡혔는지 알 수 없었음. 고른 구간을
   빨갛게 덧그림 — 범위는 **직선 + 양 끝 라운드(R)** 임. 쉬프트는 직선을 통째로 올리고
   내리므로 양 끝 호의 모양까지 함께 바뀜. 구간 시작 라운드의 BVC부터 끝 라운드의 EVC
   까지 계획선 샘플을 그대로 덧그려 곡률이 따라옴. 경계에는 점선 세로 표식.

   공용 종단 렌더러(`B06_Section_UI_Longitudinal`)는 건드리지 않고 별도 오버레이로 얹음
   (`B05_Profile_UI_Profile_RunHighlight.ts`). Y 매핑은 그 렌더러가 넘겨 주는 축 눈금에서
   되짚어 같은 스케일을 씀. 편집 버튼층(z-index 4) 아래(3)라 버튼을 가리지 않음.

`tsc --noEmit` 통과, 전체 테스트 183건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:42:23 +09:00
eomsangdonandClaude Opus 5 68e0a1c67f feat(B03): 고르는 자리 한 줄 · 완료 안내는 좌측 패널 · 결과 목록 제거
사용자 지시 3건(2026-09-03).

- 재접속 현황 배너(「…모두 완료된 프로젝트입니다」)를 좌측 안내 패널 「고르는 방법」
  안으로 옮김 — 고르기 전에 읽을 것과 같은 자리.
- [입력 파일 선택]·[임시 보관함에서 불러오기]·[파일 업로드]를 한 줄(`b03-file__pick-row`)로
  묶음. 좁아지면 접히되 선택 영역이 남는 폭을 먹음.
- 업로드 완료 항목 목록 삭제 — 같은 내용을 카드가 완료 상태로 이미 보여 줌.
  쓰이지 않게 된 `renderUploadResults`와 `.b03-file__results` 규칙도 제거.

곁들여 고친 것 — 카드 격자를 2열 고정에서 `auto-fill minmax(260px, 1fr)` 로 바꿈.
안내 패널을 연 상태에서 카드 폭이 210px 로 눌려 제목이 글자 단위로 접혔음(실측).
제목은 한 줄 말줄임으로 둠.

검증: 한 줄 배치 실측 — 선택 397px · 보관함 331px · 업로드 102px, 높이 76px 로 정렬.
카드 66px(빈 칸)/117px(파일 있음), 제목 줄 높이 21px(한 줄). typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:39:52 +09:00
eomsangdonandClaude Opus 5 c2b3c56664 fix(B03): 컨테이너 패딩 복구 — 테마에 없는 --spacing-20 사용
`padding: var(--spacing-20)` 은 정의되지 않은 토큰이라 선언째 무효가 되어 카드
컨테이너의 패딩이 통째로 사라졌음(2026-09-03 사용자 보고). 16px 토큰으로 교체.

검증: 실측 계산값 — 컨테이너 16px, 카드 12/16px, 선택 바 12/16px, 안내 문단 12px.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:35:29 +09:00
eomsangdonandClaude Opus 5 b8aa5ce014 fix(B05): 종단 기준 절·성토 불균형 표시 삭제 + 유토곡선 깜빡임 제거 + 쉬프트 안내
사용자 지적 3건.

1. 상단 표시줄의 「절·성토 불균형」은 **종단 기준** 값이었음 — 계획선과 지반선 사이 세로
   면적(㎡)의 비라 실제 토량이 아니고 판단 근거가 안 됨. 삭제함. 의미 있는 균형은 횡단
   단면적을 쌓아 부피(㎥)로 내는 하단 유토곡선 요약이고 그쪽은 그대로 둠.

2. 계획고를 연속으로 누르면 유토곡선이 매번 사라졌다 다시 서서 깜빡였음. 재계산 대기
   중에 곡선을 **지우지 않고 직전 것을 그대로 두며**, 요약줄 끝에 「다시 계산 중…」
   칩만 붙임. 재계산이 브라우저 안에서 수십 ms에 끝나므로 화면이 비는 구간이 없어짐.
   아직 한 번도 못 그렸을 때만 예전처럼 문구로 자리를 채움.

3. [쉬프트]에서 아무것도 안 골라지던 것은 **직선화된 구간이 없어서**였음(초기 계획선은
   마디마다 기울기가 달라 직선 구간이 0개). 안내를 갈라 적음 —
   「직선화된 구간이 없습니다 — [직선화]로 먼저 만드세요」 / 「…고르세요 (여러 개 가능)
   — 끝내려면 [쉬프트]를 다시 누르세요」. 직선화 안내에도 취소 방법을 덧붙임.

전체 테스트 183건 통과, `tsc --noEmit` 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:32:46 +09:00
eomsangdonandClaude Opus 5 a059c5f152 feat(B03): 파일 입력 양식 정리 — 안내는 좌측 패널, 본문은 고르는 자리만
사용자 지적(2026-09-03) — 선택 항목이 너무 크고 대중없이 놓임. 안내는 B04·B05 와 같은
공용 좌측 패널로 빼고 본문은 고르는 자리만 남김.

- `B03_FileInput_UI_Guide.ts` 신설 — 필요한 파일 / 고르는 방법 / 알아 둘 것 3묶음을
  공용 오버레이 `optionsContent` 에 실음. 문단 구획은 공용 `ui-sidebar-section`.
- 선택 영역 160px 블록 → 한 줄 바. 긴 문구는 패널로 옮기고 힌트는 한 줄로 교체.
- 카드 `min-height: 220px` 폐지, [선택]을 제목 줄 오른쪽 끝 작은 버튼으로 이동,
  빈 칸은 제목 줄만 남김. 진행바·속도·예상완료는 올라가는 동안만 표시.
- 여백 정리 — 컨테이너 패딩 32 → 20, 그룹 제목 24 → 16px, 격자 간격 24 → 12.
- 좌측 패널이 열리면 본문을 밀어내는 규칙 추가 — B03 은 일반 레이아웃이라 공용
  워크플로 레이아웃의 padding 규칙이 없어 패널이 카드를 덮었음.

검증: 카드 높이 232 → 66px(빈 칸)·117px(파일 있음), 선택 영역 160 → 47px,
문서 높이 1408px 로 한 화면 남짓. typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:31:00 +09:00
eomsangdonandClaude Opus 5 0f068a6c1d fix(B05): 구조물 측점 ▲▼ 버튼이 두 벌 생기고 가짜 변화점을 만들던 문제
같은 구조물 측점이 두 좌표로 존재했음 — 종단 정본은 원값(`85.594514`), 사이드바 목록은
표기 규칙(`n+ 0.0`)대로 0.1m 로 반올림한 값(`85.6`). 중복 제거가 누가거리 키(소수 3자리)
비교라 서로 다른 측점으로 잡혀 버튼이 두 벌 섰음.

문제는 표시만이 아니었음 — 사이드바 쪽 버튼을 누르면 정본 측점에서 몇 mm 떨어진 자리에
**가짜 변화점**이 승격돼 그 구간 기울기가 수천 %로 튀었음(실측 11,858%).

겹침 판정을 키 비교에서 **거리 비교**(0.1m 이내면 같은 측점)로 바꿈. 사이드바 목록이
0.1m 반올림이라 최대 0.05m 어긋나고, 실제로 그만큼 붙은 별개 측점은 없음.

실측(route 132, 관 11개): ▲ 버튼 75 → 65개, 근접 중복쌍 9건 → 0건.
전체 테스트 183건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:23:36 +09:00
eomsangdonandClaude Opus 5 3d037d869e feat(B05): 종단 3D 뷰어에 방위 나침반 추가 + 나침반 위젯 공용 이관
B05 3D 뷰어에는 방위 표시가 없어 방향을 읽을 수 없었음(사용자 지적, 메인 창 경유).
B04 가 오늘 새로 만든 「지면에 누운 나침반 링」을 그대로 쓰되, 두 페이지가 함께 쓰도록
공용으로 옮김.

- `B04_PreProcess_UI_Compass.ts` → `ui_template/ui_template_compass.ts` (git mv).
  `createTerrainCompass({ sizePx, className })` 로 크기·자리를 호출부가 정함.
- 스타일도 `ui_template/ui_template_compass.css` 로 옮기고 모듈이 직접 물고 옴.
  **놓을 자리만** 페이지 CSS 몫 — B04 우하단(`b04-surface__compass`),
  B05 는 ISO 버튼 아래(`b05-route__compass`).
- B05 뷰어는 104px 로 쓰고 렌더 루프에서 카메라 오프셋으로 갱신, 지형이 없으면 감춤.
  좌표 규약이 B04 와 같음(북 −z · 동 +x · 표고 +y, `Viewer.ts:295` 정점 변환) — 그대로 맞음.

화면 검증(5174): B05 나침반 104×104, ISO 버튼 아래 8px. 도북 화살이 뷰 전환에 반응 —
TOP 에서 `M0.00 -33.30`(수직), ISO 에서 `M23.56 -13.60`. 후자는 카메라 오프셋 (d,d,d)
에 대한 투영 이론값(x=R/√2=23.56, y=−R/√6=−13.60)과 일치. B04 쪽도 그대로(84×84,
우하단 16px, 링·화살 색 토큰 유지). `tsc --noEmit` 통과, 뷰어 693줄로 제한 안.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:18:51 +09:00
eomsangdon 6313d2cf58 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-03 20:13:26 +09:00
eomsangdonandClaude Opus 5 91a0157324 fix(B05): 유토곡선이 편집 중 계속 「다시 계산 중」에 머물던 문제
낡음 판정이 **편집 전 계획선**과 견주고 있었음. 로컬 재계산은 편집된 계획고로 횡단
설계를 갱신하는데 비교 대상은 저장분이라, 편집이 있는 동안 판정이 영원히 참으로 남아
유토곡선이 빈 화면에 문구만 남았음(사용자 보고).

- 그리기(`_Profile_Render`)와 진입 판정(`_Profile_Panel.render`) 모두 편집이 반영된
  계획선(`toDesignProfile(alignment, …)`)과 견주도록 바꿈. 특히 진입 판정은 세션에
  미저장 편집이 남은 채 새로고침하면 「어긋남 없음」으로 나와 재계산을 예약조차 하지
  않았음 — 그래서 새로고침해도 문구가 그대로였음.
- 세월교 측점은 노면이 월류 높이만큼 일부러 내려 앉으므로, 판정에서 `surface_drop_m`
  을 더해 비교. 안 그러면 계획선을 안 건드려도 늘 어긋난 것으로 나옴.

화면 검증(공용 브라우저 5174, route 132): [초기화] 직후 문구 없음·유토곡선 정상
(절토 1,113.5㎥ / 성토 1,424.1㎥). 계획고 ▲ 1클릭 시 문구 없이 즉시 갱신
(절토 1,108.3㎥ / 성토 1,428.6㎥), ↶ 로 되돌리면 원값 복귀. 임시 진단 코드는 제거함.
전체 테스트 183건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:12:34 +09:00
eomsangdonandClaude Opus 5 b5de29679f feat(B04): 방위 표시를 지면에 누운 나침반 링으로
시안 3종 비교 뒤 사용자 선택(2026-09-03) — TerriaJS·cesium-navigation 계열 표기.
링·눈금·도북 화살을 지면 평면 도형으로 두고 카메라에 투영하므로, 링이 눌린 정도가
곧 시점 기울기이고 도북은 늘 화면의 실제 북쪽을 가리킴.

- 15° 눈금(90°마다 긴 눈금), N·E·S·W 라벨(뒤로 넘어가면 흐림).
- 표고축 침은 내려다볼수록 짧아지고, 점으로 눌리면 Z 라벨을 감춤.
- 위젯 56 → 84px — 라벨이 읽히는 최소 크기.

검증: typecheck·prettier 통과. 리로드 실측 — 위젯 84×84, 링 경로 `M0.00 −33.98…`,
도북 화살 `M0.00 −33.30 L−4.28 −17.14 L4.28 −17.14Z` 로 기본 시점 계산값과 일치.
회전 중 거동은 사용자 화면 검증 대기.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 20:07:29 +09:00
eomsangdonandClaude Opus 5 a35c8ff356 feat(B05,B06): 계획선 편집 중 횡단 재계산을 브라우저에서 수행 — 서버 왕복 제거
사용자 조작 중의 계산은 브라우저 안에서 끝나야 함(2026-09-03 사용자 확정). 계획고가
바뀔 때마다 전 측점 횡단 계산이 `POST …/cross-design/preview` 로 나가 왕복이 조작
속도를 지배했음.

- `refreshCrossDesigns` 를 로컬 계산으로 교체. 계획고는 저장 선형(`base_pvi`)에 편집
  델타를 얹어 TS `buildAlignment` 로 여기서 풂 — 호출부(B05·B06) 시그니처는 그대로라
  단일 창구가 유지됨. 서버 프리뷰는 선형 저장분이 없는 옛 데이터 폴백으로만 남음.
- 표준단면 config 기본값·암 경계 기본 오프셋을 `sections/context` 응답에서 세션에 기억
  (`rememberStandardDefaults`·`rememberRockBoundaryDefault`). 프론트에 config 수치를
  복제하지 않으면서 패널 없는 B05 도 같은 값으로 계산함.
- 디바운스 250 → 60ms — 왕복이 사라져 손을 떼는 즉시 유토곡선이 따라옴.
- 낡음 판정을 `hasStaleDesigns` 로 바꿔 첫 건에서 중단하고, 계획선 유효 샘플을
  샘플 배열당 한 번만 걸러 이진탐색(그리기 프레임당 2.33ms → 0.07ms 실측).
- 파이썬 엔진 2파일·프리뷰 라우터에 「TS 짝 파일과 한 벌」 경고 블록 추가.

검증: `tmp/tests/test_b06_cross_design_mirror.py` 10건 — 토사/암 2단계/L형 측구/포장/
세월교 하강/사면 미닫힘 등에서 파이썬과 TS 결과가 면적·설계선까지 일치. 전체 183건 통과.
`tsc --noEmit` 통과. 실측: route 127(측점 128) 전 측점 로컬 재계산 7.5ms.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 19:58:09 +09:00
eomsangdonandClaude Opus 5 dcabce147e feat(B04): 지표면 3D 방위 표시를 3축 트라이어드로 교체
옛 콤파스는 방위각(`atan2(ox, oz)`)만 써서 경사에 따른 남북 압축이 빠졌음 —
사시도 프리셋(오프셋 120·95·135, 앙각 27.7°)에서 바늘 41.6°, 화면 실제 북쪽 62.4°로
20.8° 어긋났음(2026-09-03 사용자 지적).

세계 축 N(−z)·E(+x)·Z(+y)를 카메라 화면 기저(오른쪽 r, 위 u)에 투영해 선과 글자를
그림. 기저는 카메라 오프셋만으로 구함(up = +y 고정). 뒤로 누운 축(깊이 > 0.25)과
정면으로 와 짧아진 축은 흐리게 그려 겹침을 줄임.

검증: typecheck 통과. 기본 시점에서 DOM 좌표가 해석값과 일치 —
N (28.0, 11.0) · E (45.0, 28.0) · Z (28.0, 27.4). 회전 중 거동은 사용자 화면 검증 대기.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 19:54:13 +09:00
eomsangdon de41958446 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-03 19:42:39 +09:00
eomsangdonandClaude Opus 5 c19af779e4 feat(B06): 횡단 설계 계산 TS 미러 추가 — 브라우저에서 절·성토 면적 산출
사용자 조작 중 계산은 브라우저 안에서 끝나야 함(2026-09-03 사용자 확정). 계획선을
만질 때마다 전 측점 횡단 계산이 서버로 나가 조작 속도를 왕복이 지배했음.

- `common_util_cross_design_areas.ts` — `B06_Section_Engine_Areas.py` 미러
  (사다리꼴 적분 + 절토 토사/암반 분리).
- `common_util_cross_design.ts` — `B06_Section_Engine_Design.py` 미러
  (표준횡단 설계선 구성 + 단면적). config 수치는 복제하지 않고
  `sections/context.standard_cross_section` 을 입력으로 받음.

두 파일 머리에 짝 파일·회귀 테스트를 가리키는 경고 블록을 둠 — 한쪽만 고치면 두 화면
값이 갈림. 아직 어느 화면도 이 모듈을 부르지 않음(다음 커밋에서 연결).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 19:42:31 +09:00
eomsangdonandClaude Opus 5 b908a0b10d feat(B06): 종단면도 구조물 라벨도 배수관 표기로 통일
관종·관경(`파형강관 D1200`) 대신 `배수관` 을 적음 — 횡단 카드와 같은 규칙.
판정을 `structureDisplayName()`(`_UI_Section_Common`) 한 곳으로 모아 두 렌더러가
같은 문자열을 쓰게 함. 저장 라벨은 그대로 — 재진입 이관이 라벨로 종류를 되짚음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 19:35:21 +09:00
eomsangdonandClaude Opus 5 f772401b06 feat(B06): 횡단 카드 구조물 pill 을 배수관 표기로 통일
관종·관경(`파형강관 D1200`) 대신 표시 이름 `배수관` 하나만 적음 —
2026-08-17 확정한 `PIPE_DISPLAY_NAME` 규칙과 같은 표기. 관종·관경은 툴팁에 남김.
판정은 `PIPE_TYPES` 목록으로 하고 저장 라벨은 손대지 않음 — 재진입 이관이
라벨 문자열로 구조물 종류를 되짚기 때문임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 19:28:10 +09:00
eomsangdon c21b68f2ce @
feat(B06): 횡단 카드 제목행에 성토사면 본래 경사길이 표기

사면 미교차 경고가 붙는 제목행 우측 끝에 성토사면 경사길이를 표기함.
값은 정본 설계선(`design_line`) 기준이라 기슭막이·집수정이 사면을 끊기 전
**본래 길이**이고, 양측 성토는 좌·우 두 값을 다 냄.

- `fillSlopeLengths()` 신설 — 기존 `protectedSpan`·`meetOffset` 재사용,
  성토측만 노견 끝(측구가 있으면 측구 바깥)에서 사면 끝까지 `run × √(1+1/n²)`.
- 미교차 측점은 `≥` 하한값 — 추세 외삽은 지반이 사면과 나란한 자리에서
  수십 m 씩 튀어(740m 측점 101.53m) 길이 표기에 못 씀.
- `meetOffset` 교차점을 두 걸음 사이 선형보간으로 냄 — 반폭 맞춤(1m 올림)에는
  영향이 없고 길이 표기가 탐색 간격만큼 튀는 것만 막음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@
2026-09-03 19:16:46 +09:00
eomsangdonandClaude Opus 5 0ce6b43bdc fix(B05): 전체 측점 계획선에 종단곡선 복원 — 호가 지반고를 통과하도록 꼭짓점 반복 보정
`3651ad98` 의 `only_explicit_curves=True` 로 전체 측점 폴리라인에서 자동 종단곡선이
전부 꺼져, B05 종단 그래프의 계획선이 직선만으로 그려졌음.

- `design_ground_following_profile()` — 변화점마다 기본 L(15m) 종단곡선을 다시 삽입.
  대칭 종단곡선은 꼭짓점을 지나지 않으므로(중앙종거 |A|·L/8), 호와 측점 세로선의 교점이
  지반고가 되도록 꼭짓점 표고를 최대 12회 반복 보정 — `design_pipe_anchored_profile` 과
  같은 방식. 결과: 호의 중심이 측점 세로선 위, 좌우 직선과 접선, 계획고 = 지반고.
- `rebuild_alignment_profile()` — `GROUND_POLYLINE_BASIS` 곡선 생략 예외 제거.
  저장된 `base_pvi` 가 보정된 꼭짓점이라 편집 델타를 지우면 초기선으로 그대로 복귀함.
- 프론트는 payload `only_explicit_curves` 를 그대로 따르므로 코드 변경 없음.
- 호 크기 기준은 현행 유지(2026-09-03 사용자 결정) — L 고정·R 은 결과값, 법정 최소반경
  미만은 경고만.

검증: `tmp/tests/test_b05_ground_profile_curves.py` 4건 + 전체 173건 통과. route 127
실데이터 오프라인 산출에서 호 126개(저장분 0개), 측점 계획고−지반고 최대 0.063mm.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 19:02:03 +09:00
eomsangdon 0f5b4e4df7 auto: 2026-09-03 18:31 (ESD_LAPTOP) 2026-09-03 18:31:54 +09:00
eomsangdonandClaude Opus 5 4fdbcaf09c fix(B07): 장 확정 수량표 결함 3건 — 측점 구분·계획고·확정 해제
장 단위 확정 흐름을 실측하다 드러난 것들이다(2026-09-03, 용화 검증본 3장).

① **측점 구분 없이 역추출** — `_table_values_from_cells()` 가 도면의 표를 전부 훑어
   마지막 값을 모든 측점에 넣었다(실측: 4개 측점 전부 지반고 837.21, 도면에는
   840.87·840.33·836.45·837.21 로 제대로 그려져 있었음). 표 엔티티 id 가
   `uuid5("{도면id}:qtable")` 이라 측점을 되짚을 수 있어 그 표만 읽는다. 옛 도면은
   id 가 안 맞으므로 종전처럼 전부 훑는 폴백을 남긴다.

② **계획고가 빈 채로 나감** — 장 배치 입력의 원본에는 계획고가 없어 계획고·절토고·성토고
   세 칸이 통째로 비었다(21개 항목 중 지반고 하나만 채워짐). `_quantity_table()` 이
   횡단 설계(`design.design_elevation_m`)도 보게 했다.

③ **확정 해제가 404** — [수정]이 도면 목록을 **설계값 없이** 만들어 장 나눔이 달라졌고,
   방금 확정한 장 id 가 목록에 없어 되돌릴 수 없었다. 목록 조회·확정과 같은 인자를 쓴다.

검증(공용 브라우저·실동작) — 확정 해제 404 → **200**, 표 값 `planned=840.87 fill=0.00` 등
측점마다 다름, manifest 수량표가 측점별로 갈림(220 · 240 · 260 · 264 · 280 각각 4/21 항목
채움 — 나머지 17칸은 사용자가 CAD 에서 채우는 자리). 시험 뒤 확정은 모두 해제해 원상복구.
pytest 383 passed·17 skipped, ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:43:35 +09:00
eomsangdonandClaude Opus 5 e29e3cab67 feat(B07): 유역도 정보표 측점 표기도 n+ 0.0 으로 통일
토적도만 납품 표기(`120+ 0.0`)로 바꿔 두어 유역도 정보표는 `No.6` 으로 남아 있었다
(2026-09-03 사용자 지시로 통일). `station_plus_label()` 을 토적도 전용 모듈에서 공용
`_Engine_Cad` 로 올려 두 도면이 같은 함수를 쓴다. 종단도 표·횡단도 제목의 `No.n` 표기는
그대로 둔다 — 이번 지시 범위가 아니다.

검증 — 유역도 정보표 첫 줄 `(1) No.6` → `(1) 6+ 0.0`. pytest 383 passed·17 skipped,
ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:09:14 +09:00
eomsangdon 95fdd34351 Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1 2026-09-03 15:50:22 +09:00
eomsangdonandClaude Opus 5 090442bb00 fix(B04): 유역 경계 단순화 허용오차 2.0m → 1.0m (격자 한 칸)
유역 격자가 1m(DRAINAGE_GRID_SIZE_M)라 경계는 1m 계단이다. 허용오차를 격자 한 칸에
맞추면 형상 손실이 격자 오차 안으로 들어가고, 유역별로 따로 단순화해 생기던 공유 경계
어긋남도 줄어든다.

실측(min_area=0 상태, 용화/S자):
- 좌표점        622·829   → 1,749·1,987
- 면적오차      1.86%·1.42% → 1.01%·0.67%
- 유역끼리 중첩 1,043㎡·1,373㎡ → 243㎡·373㎡
- 틈            1,184㎡·2,579㎡ → 369㎡·668㎡
0.5m 이하는 1m 계단 점을 지우지 못해 좌표점만 6배(10,215)로 튄다.

브라우저 실측도 같은 값(API 좌표점 1,750·1,987, 경계 없는 유역 0개)이고 확대해도 계단이
도드라지지 않는다. 테스트 159 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 15:50:12 +09:00
eomsangdonandClaude Opus 5 5331167439 refactor(B05): 구조물 배치 폼 뼈대를 전용 조립기로 분리 (700줄 제한 1차)
`_UI_Structures_Panel` 1,078줄 → 1,024줄. 화면 요소를 세우는 몫만 `_UI_Structures_Form`
으로 옮기고 패널은 값·검증·저장·목록을 맡는다. 배치 규칙(행 순서·구분선·버튼 묶음)은
사용자 지시로 굳은 것이라 그대로 옮겼다.

아직 제한을 넘는다 — 남은 큰 덩어리는 `commit()` 약 200줄과 옵션 필드 렌더·시설 폼
로드 약 170줄이다. 둘 다 폼 상태를 깊게 만지므로 [추가]·[삭제] 실동작 검증을 붙여
따로 옮긴다.

검증 — 공용 브라우저 B05 「구조물 배치」 실측: 제목·측점 행·메모 칸·목록 1개,
버튼 [추가]·[삭제]·[리셋], 구조물군 7항목 그대로. pytest 383 passed·17 skipped,
typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 15:47:16 +09:00
eomsangdonandClaude Opus 5 754e00de19 fix(B07): 장 선택 시 정보 패널을 장 단위로 표기
장(여러 측점을 담은 횡단 도면)에는 측점 단위 지반·계획 값이 없는데도 제목이
「측점 3장 (220~264m)」으로 달려 어느 측점 값인지 오해됐다. 장이면 제목을 「장」으로 바꾸고
「이 장은 측점 여러 개를 담습니다. 지반·계획 정보는 측점 도면에서 봅니다」로 안내한다.

판정은 도면 id(`cross_s…` = 장) 로 한다 — `chainage_m` 은 장에도 첫 측점 값이 실려 와
그것으로는 갈리지 않는다(실측).

검증 — 공용 브라우저 B07: 3장(220~264m) 선택 시 `장 3장 (220~264m) · 잠정 · 이 장은 측점
여러 개를 담습니다…`, 종단도 선택 시 패널 없음(종전과 같음). typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 14:06:11 +09:00
eomsangdonandClaude Opus 5 15c2d81b49 refactor(B06): 횡단 카드 껍데기·반폭 계산 분리 (700줄 제한)
`_UI_Cross_View` 784줄 → 698줄. 도면과 무관한 제목행·하단 정보행을 `_UI_Cross_Card_Chrome`
으로 옮기고, 실효 표시 반폭 계산은 순수 계산이라 `_UI_Cross_View_Metrics` 로, 측점별 줌·팬
상태 맵은 임자인 `_UI_Cross_View_Zoom` 으로 옮겼다. 계산·판정 규칙은 그대로다.

검증 — 공용 브라우저 B06 실측: 카드 65·제목행 65·하단행 65·단면유형 pill 65·사면 미교차
경고 13, 첫 카드 제목행 `0+0.0 / 0.0m / 토사·리핑암·발파암 / 편측 성토 / BP` 로 종전과 동일.
pytest 383 passed·17 skipped, typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:56:01 +09:00
eomsangdonandClaude Opus 5 d6d211ef61 refactor(B06): 유토곡선 패널 조립을 전용 모듈로 분리 (700줄 제한)
`_UI_Section_View` 751줄 → 689줄. 곡선 계산 → 차트 → 범례 → 요약줄까지 한 덩어리를
`_UI_Section_View_MassHaul` 로 옮기고, 뷰 컨트롤러는 **붙일 자리**만 정한다(차트는 종단면도와
같은 부모의 형제, 범례는 스크롤 컨테이너 밖). 낡음 판정·계산은 옮긴 자리에서도 공용 모듈
그대로라 동작이 바뀌지 않는다.

검증 — 공용 브라우저 B06 실측: 요약줄 문자열 동일
(`절토(자연) 4,526.8㎥ … 최종 누가토량 −10,213.3㎥`), 범례 1·차트 1·카드 65·상태줄 빈 문자열.
pytest 383 passed·17 skipped, typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:50:53 +09:00
eomsangdonandClaude Opus 5 451ed97279 feat(B07): 돌쌓기 해칭 CAD 반영 + 표제란 SVG 서명 벡터 변환
해칭 — 수확기가 `g[clip-path]` 를 통째로 건너뛰어 기슭막이 형태 해칭(돌쌓기·콘크리트·
돌망태·통나무·바자)이 CAD 도면에 하나도 실리지 않았다. 클립을 쓰는 대신 **경계로 잘라서**
싣는다(2026-09-03 사용자 결정). 선분–폴리곤 교차로 안쪽 조각만 남기고, 클립 안 원(통나무
마구리)은 36각 폴리선으로 펴서 같은 방식으로 자른다. 문자는 중심점이 안일 때만 싣는다.

서명 — SVG 로 올라온 자산만 CAD 폴리선으로 바꾼다(2026-09-03 사용자 결정). DXF 로 나가도
선으로 남고 확대해도 깨지지 않는다. `B07_DesignDetail_Engine_Cad_Svg` 신설(path M·L·H·V·
C·Q·S·T·Z 지원, 곡선은 16분할, viewBox 비율 유지·y 뒤집기). PNG·JPG 는 그릴 선이 없으므로
종전대로 ImageEntity, 읽기 실패도 원래 그림을 그대로 둔다.

검증 — `tmp/tests/test_b07_signature_vector.py` 5건 신설(파싱·비율·엔티티 교체·래스터 유지·
손상 SVG), 전체 383 passed·17 skipped. typecheck·prettier·ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:46:54 +09:00
eomsangdonandClaude Opus 5 ce4fcc1f13 feat(B07): 토적도 길이별 자동 축척 + 납품 도면 표기 8행 반영
축척 — 노선 연장에 맞춰 **한 장에 들어가는 가장 큰 그림**을 고른다(2026-09-03 사용자 결정).
후보는 도면 관행 축척(1:500~1:6,000, `DRAWING_SCALE_MASSHAUL_H_CANDIDATES`)이고 A1 작도영역
가용 폭 700mm 를 기준으로 판정한다. 실측 — 40m 노선 1:500 · 용화 1,106m 1:2,000 ·
3,465m 1:5,000. 세로는 종이 1mm=50㎥ 고정(도면끼리 비교하려면 같아야 한다).

표기 8행(사용자 확정) — ① 장비명 무대→종무대 ② 값 표기 `Q= 162.41M3`(등호 뒤 한 칸)
③ 지시선 계단형·balloon 모서리 접점 ④ 평형선·띠 경계현 빨강 ⑤ 측점 `120+ 0.0`
⑥ 표 눈금 신설(정규 빨강·추가 회색) ⑦ 행 이름 3.2mm·자간 벌림 ⑧ M.N 소수 2자리 `0+16.90`.

`MM_H` 상수를 없애고 `mm_h` 를 그리기 함수에 넘긴다 — 축척이 노선마다 달라졌기 때문.

검증 — `tmp/tests/test_b07_masshaul_basin.py` 갱신·신설(축척 3건·표기 8행·지시선·눈금),
전체 378 passed·17 skipped, ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:37:05 +09:00
eomsangdonandClaude Opus 5 2aaa0c594d feat(B05): 세월교 접근선 구간을 3D 패치 리본에 포함
노견 → 벽 상단 접근선 구간이 패치에서 빠져 있어 좁은 좌측 패치 사이로 원지반이 드러나
보였다(2026-09-03 사용자 확정: 「패치로 덮기」). 접근선은 벽보다 위라 덮어도 관 구멍·L자
통로를 막지 않는다 — 벽 전면·유로는 종전대로 리본에 담지 않는다(2026-08-25 사용자 ②).

접근선 줄을 `patchPoints` 앞에 담고 `fillRuns` 에 한 장으로 실어 「구간 1줄 = 리본 1장」
규칙을 그대로 따른다.

검증 — typecheck·prettier 통과. 3D 육안 확인은 카메라 조작이 필요해 다음 화면 검증
묶음에서 함께 수행.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:29:29 +09:00
eomsangdonandClaude Opus 5 e83171b2b3 refactor(공통): 작업 좌표계 폴백을 창구 하나로 통합 + 트림 실패 진단
같은 폴백 사다리가 네 곳에 서로 다른 모양으로 흩어져 있었고, 그중 배수유역 라우터
`_route_center_lonlat` 은 노선 CSV 의 `crs_epsg` **라벨**을 그대로 변환에 썼다(라벨과
실좌표계가 다른 사례 실측 — 2026-09-01 용화 라벨 5179 / 실제 5176).

`common_util_crs.resolve_project_crs()` 신설 — 사다리는 ① 노선 crs_input ② 파일 라벨
(원본 좌표를 읽는 자리만) ③ 지형 PRJ(작업 좌표계 정본) ④ DB epsg ⑤ EPSG:5186.
작업 좌표계는 **지형 PRJ 우선으로 확정**(2026-09-03 사용자 결정 — 서피스 격자가 모델좌표의
주인이라 현행 유지). `project_epsg_from_prj()` 도 이 창구로 위임.

더해 지표면 트림이 노선을 통째로 지울 때 노선·지표면 bbox 를 함께 로그에 남긴다 —
"겹치지 않습니다" 만으로는 좌표계 문제인지 측량 범위 문제인지 갈리지 않았다.

검증 — `tmp/tests/test_project_crs_resolution.py` 5건 신설(사다리 4·진단 1),
전체 375 passed·17 skipped, ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:25:20 +09:00
eomsangdon f91d589ed4 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-03 13:16:14 +09:00
eomsangdonandClaude Opus 5 8393dfd619 fix(B04): 세부유역 최소면적 필터 제거 — 작은 유역이 통째로 안 그려지던 문제
100㎡ 미만 유역은 `polygonize_labels` 의 최소면적 필터에 걸려 폴리곤이 아예 만들어지지
않았다. 유역 목록에는 있는데 지도에는 없는 상태 — 실측에서 용화 22개 중 1개(64㎡),
S자 48개 중 1개가 그랬다. 링 목록이 조각을 싣게 된 뒤로는 버릴 이유가 없다.

실측: 폴리곤 면적 오차(셀 면적 대비 평균) 용화 5.76%→1.86%, S자 3.56%→1.42%,
경계 없는 유역 1개→0개. 좌표점은 583→622 · 806→829 로 거의 그대로다.
1차 전체 배수유역 등 다른 호출부의 기본값(100㎡)은 그대로 둔다.

S자 실사례 검증: 용화 지형 위에 스위치백 3구간 합성 노선(정점 439·연장 4,528m)으로
신규 프로젝트를 만들어 자동 체인까지 완주 — 관 48개·세부유역 48개, 경계 없는 유역 0개.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:13:00 +09:00
eomsangdonandClaude Opus 5 f1bcee7857 feat(B04): 세부유역 경계를 링 목록(외곽+구멍)으로 넓혀 중첩·빈공간 해소
단일 링 자료형이 곧 중첩·빈공간이었다. 아래 유역이 위 유역을 감싸면 구멍이 사라져 위
유역을 통째로 덮고(합성 실측 256㎡ 전량), 한 관의 유역이 두 조각이면 작은 쪽이 사라져
빈공간이 됐다(144㎡).

- `polygon_parts()` 신설 — 조각마다 [외곽 링, 구멍 링...], 넓은 조각부터.
- `WatershedBasin.boundary_parts` 로 교체. `boundary_xy`(가장 넓은 조각의 외곽)와
  `boundary_rings`(편 링 목록)는 파생 속성이라 옛 소비처는 그대로 동작한다.
- API 에 `polygon_rings_lonlat` 추가. 저장 GeoJSON 은 구멍을 가진 Polygon, 조각이
  여럿이면 MultiPolygon.
- 캔버스 4곳(B04 유역화면 채움·선택, B05 배수유역도 채움·선택)은 even-odd 로 한 번에
  채우고 `pointInRings()` 로 판정 — 구멍 안을 눌러도 바깥 유역이 잡히지 않는다.
  중복이던 지역 `pointInRing` 은 삭제하고 공용 것으로 통일.

검증: 단위 5건 신규(도넛 구멍 보존·조각 2개 보존·파생 속성·빈 경계·조립 경로 전체에서
링 2개), 전체 168 passed, typecheck 통과. 브라우저 — API 가 유역 22개에 링 목록을 실어
보내고, 캔버스 even-odd 실측(가운데 알파 0 / 고리 255)으로 구멍이 실제로 뚫린다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 12:40:21 +09:00
eomsangdonandClaude Opus 5 a7f6232c01 fix(B06): I형 집수정 관 하단을 벽면 안으로 제한 — 관이 벽을 관통하게
관 하단 자리는 벽 계류측 변과 원지반의 교점인데, 원지반이 벽 상단보다 높은 자리에서는
교점이 벽 상단으로 밀려 관이 벽 위에 얹혔다(측벽과 배관이 만나지 않음). ㄴ·ㄷ형은 관이
바닥판 윗면에서 시작해 벽 안에 물리는 것과 대비된다.

교점을 벽 상단에서 관경+여유(0.5m)만큼 아래로 제한한다. 원지반이 그보다 낮으면 종전대로
교점을 그대로 쓴다. 벽을 키우는 방향은 벽이 1:0.3으로 기울어 base가 계류측으로 밀리며
지반 교점이 함께 올라가 벽이 7.5m까지 자라 폐기했다.

검증 — 공용 브라우저 B06 I형 8개 단면 실측: 관 유입측 끝단과 벽의 세로 겹침
18.1~56.4px(수정 전 0 — 벽 위로 이탈). ㄴ형 대조(13+4.1을 조정창에서 ㄴ형으로 바꿔 확인)
겹침 유지, 확인 후 세션값 원복. pytest 370 passed·17 skipped, typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 10:32:20 +09:00
eomsangdonandClaude Opus 5 d140d04766 fix(B03): [LAS 없이 설계] 토글이 업로드 버튼 판정을 다시 돌리게 수정
이 토글은 LAS 카드의 필수 여부를 바꾸는데(isSlotRequired) change 핸들러가
updateUploadButton() 을 부르지 않아, 파일을 먼저 고르고 토글을 나중에 켜면 필수 조건이
채워졌는데도 [파일 업로드] 가 잠긴 채 남았다. 새로고침하거나 파일을 하나 더 골라야
풀렸다.

검증: 공용 브라우저에서 저장된 토글 표시를 지워 첫 진입 상태로 재현 — 파일 7개만 고른
상태는 비활성(안내: LAS/LAZ 필요), 토글을 켠 직후 즉시 활성, 다시 끄면 즉시 비활성.
typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 10:29:09 +09:00
eomsangdonandClaude Opus 5 16a24ace3d feat(B04): 지표면 3D 뷰어에 방위 콤파스 추가
뷰어 좌표 규약이 `x, 높이, -y` 라 세계 북쪽은 -z 다. 카메라 오프셋(카메라 − 타깃)의
수평 성분으로 도북이 화면에서 몇 도 돌아갔는지 계산해(atan2(offset.x, offset.z)) 바늘을
돌린다. 뷰어 우하단 — 축척 막대(좌하단)의 반대편에 둔다.

뷰어 파일이 이미 946줄이라 콤파스는 별도 모듈로 분리(700줄 제한). 애니메이션 루프에서
0.5° 이상 움직였을 때만 transform 을 다시 쓴다.

검증: typecheck 통과. 공용 브라우저 실측 — [상단] 시점 0°(바늘이 북쪽=화면 위),
[사시도] 41.6°, 캔버스 220px 드래그 시 218.6° 로 카메라와 함께 회전.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 10:02:15 +09:00
eomsangdonandClaude Opus 5 9b4abecf9d refactor(B05/B06): 유토곡선 일원화 — 보는 노선·횡단 재계산 창구·낡음 판정 통합
같은 데이터를 두 화면에 보여 주는 기능인데 세 곳이 갈려 값이 달랐다(실측: 절토(자연)
B06 3,704.6㎥ ↔ B05 4,526.8㎥).

① 보는 노선 — B05는 최신 경로, B06은 최신 **확정** 경로를 열어 노선을 다시 탐색한
   프로젝트에서 서로 다른 노선을 봤다(route 126 DRAFT ↔ 125 CONFIRMED, 같은 20m
   측점 성토 4.82㎡ ↔ 85.9㎡). `get_workflow_route_context()`(최신 경로) 신설해 B06
   화면 context가 그것을 쓴다. 납품 도면(B07)이 쓰는 확정 전용 창구는 그대로 둔다.

② 횡단 재계산 — 호출이 두 벌이라 인자가 갈렸다(B06만 표준 단면값·암 경계 오프셋 전달,
   보존하는 사용자 부속값도 2개 ↔ 7개). `B06_Section_Cross_Refresh.refreshCrossDesigns()`
   한 창구로 모으고 세션 편집값은 저장소에서 직접 읽어 패널 없는 B05도 같은 값을 보낸다.

③ 낡음 판정 — 「옛 암 2단계 필드 누락」 조건이 B06 페이지에만 있어 B05는 재계산을
   건너뛰었다. 공용 `staleDesignChainages()` 안으로 옮겨 두 화면이 같은 시점에 같은
   조치를 한다.

검증 — 공용 브라우저 실측: 두 화면 모두 route 126, 요약줄 문자열 완전 일치
(`절토(자연) 4,526.8㎥ · 성토 14,881.8㎥ · 토취 10,213.3㎥ · 최종 누가토량 −10,213.3㎥`).
pytest 370 passed·17 skipped(일원화 검사 4건 신설), typecheck·prettier·ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 09:50:14 +09:00
eomsangdonandClaude Opus 5 6bca521516 fix(B04): 세부 배수유역 배정을 종단 마루 분수령 기준으로 교체
옛 방식은 한 칸 이웃만 보는 국소 하강이라 1m 간격 계획고의 미세 요철에 걸려 멈췄다.
용화 실측: 측점 2,138개 중 1,898개(88.8%)가 저점에 갇혀 흐름이 아니라 누가거리
최근접으로 관에 붙었고, 그 결과 도로 셀 43.2%가 자기보다 높은 관에 배정됐다
(최대 6.82m 오르막).

이웃한 두 관 사이의 최고점(마루)을 분수령으로 삼아 왼쪽은 앞 관, 오른쪽은 뒤 관이
받게 바꿈. 1차원에서 물은 마루를 넘지 못하므로 물리적으로 정확하고 미세 요철에 면역.
첫 관 앞·마지막 관 뒤는 그 관이 받는다. 관 순서는 호출자가 준 그대로 보존.

검증: 단위 5건 신규(전체 163 passed), 실측 오르막 배정 43.2% → 17.6%(최대 4.15m),
잔여 1,993셀은 전부 사그 저류 후 더 낮은 장벽으로 넘치는 경우(어긋남 0).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 09:43:38 +09:00
eomsangdonandClaude Opus 5 23b1e7e5b0 feat(B05/B06): 옛 계획고 유토곡선 노출 차단 + 사면 미교차 측점 경고
① 저장된 횡단이 지금 계획선과 어긋나면(재계산 예약 상태) 유토곡선을 그리지 않고
「횡단을 지금 계획선에 맞춰 다시 계산하는 중입니다」만 띄움. 옛 계획고로 만든 면적이
2초간 보였다가 정본으로 갈아 끼워지던 것을 없앰(2026-09-03 사용자 확정 「새 값만
보여주기」). 용화 실측 — 65측점 중 56개가 낡음, 계획고 차 −9.36~+5.83m,
저장분 성토 42,050㎥ ↔ 정본 14,881㎥.

② 사면이 계산 반폭 끝까지 원지반과 만나지 않아 면적이 잘린 측점에 카드 경고 표기.
판정은 면적을 내는 엔진 한 곳(B06_Section_Engine_Design)에서 `slope_unclosed` 로
내려 화면·수량·도면이 같은 기준을 봄. 계산은 손대지 않음(2026-09-03 사용자 확정
「영원히 못 만나는 지형이 있을 수 있으니 경고로 대체」).

검증 — 공용 브라우저 B06 실측: 2.6초 재계산 안내만 표시(옛 값 미노출), 4.4초 정본
표시, 사면 미교차 경고 13/65측점. pytest 366 passed·17 skipped, typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 09:10:46 +09:00
eomsangdonandClaude Opus 5 56b9bf5280 feat(B03): 계획노선 shapefile 의 CSV 정본화 + LAS 없는 도엽 서피스 좌표계 교정
- 자동설계 체인이 초기값 스냅샷에 `planned_route.csv` 를 함께 남김. 값은 체인이
  이미 만든 정점(사업지 좌표계·트림·조밀화 후)이라 재계산 없음.
- `load_design_route()` 는 surface_params 가 주어진 호출에 한해 그 CSV 정본을 읽음.
  트림 전 원본이 필요한 호출(도엽 범위)은 종전 경로 유지. 지표면·노선이 바뀌면
  `discard_initial_snapshot()` 이 폴더째 지워 경로가 저절로 닫힘.
- `run_sheet_surface_analysis()` 가 노선을 원본 좌표계로 읽어 도엽 서피스를 딴 자리에
  만들던 문제 교정 — `load_design_route()` 로 창구 통일, 도엽 확보 PRJ 탐색도 지형
  PRJ 폴더로 교정. shapefile 노선(5179) + 지형 PRJ(5176) 조합에서 트림이 노선을
  통째로 지우던 원인.

검증: tmp/tests 158 passed(신규 4건 포함), 공용 브라우저 실사용 경로로 용화 shapefile
한 벌 업로드 → 자동 체인 완주 → CSV 정본 643점, 원본 경로와 최대 좌표차 0.00007m.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 08:17:21 +09:00
eomsangdonandClaude Opus 5 698f8f0505 knowledge(임도): 유토곡선 토량배분 기술문서 신설 + 미결 20·21 등재
실무 유토곡선 원본 6건(.MASS · EARTH.DAT) 파싱 관측 수록 — 최종 누가토량 +49~+324㎥,
0선 교차 3~17회, 장비 한계거리 전 건 무대 20.0 / 도자 60.0 m. 평형선 자동 선정·극값
무시 임계·balloon 표기 관행은 현행 근거 공백임을 명시하고 미결 20 으로 등재. 도자
한계거리 60m(실무) vs 70m(현행 구현) 채택은 미결 21.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 07:53:32 +09:00
eomsangdonandClaude Opus 5 0e0dd74e92 feat(B05/B06): 유토곡선 요약줄에 최종 누가토량 칩 신설
평형선·장비 띠·물량 balloon 이 안 생기는 노선에서 그 이유를 화면에서 읽을 수 있게,
곡선 끝의 누가토량을 요약 칩으로 드러냄. 0에 가까울수록 절·성토 균형이며 곡선이
0선을 넘지 않으면 평형선이 서지 않는다는 설명을 툴팁에 담음. 판정·조정은 사용자
몫이라 자동 보정 로직은 두지 않음(2026-09-03 사용자 확정).

B05 종단 패널과 B06 횡단 화면이 같은 함수를 쓰므로 양쪽에 함께 반영됨.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 07:53:32 +09:00
eomsangdonandClaude Opus 5 8daadf2d4f fix(B06): I형 집수정 배관 끝단이 마감면에 접혀 삼각형이 되던 결함 수정
관 끝단 두 꼭짓점을 마감면(I형 벽 안쪽 변) 선분 안으로 자를 때, 원지반이 벽 상단
위라 관 시작점이 벽 상단에 붙으면 위·아래 교점이 같은 끝점으로 접혀 관 단면이
삼각형으로 그려짐. 비스듬히 자른 끝단 길이는 항상 관경 이상이므로, 그보다 짧으면
접힌 것으로 보고 축 직각 마감으로 되돌림.

검증 — 공용 브라우저 B06 단면 8개 실측: 유입측 끝단 길이 0.0px → 18.7~58.4px.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 07:19:32 +09:00
eomsangdonandClaude Opus 5 e065e5701e fix(B05): 최소고 가드를 편집 단일 창구로 이관 + 되돌리기가 구조물 위치까지 복원
2026-09-02 화면 검증에서 나온 미해결 2건 처리.

- 최소고 가드: `blocksMinCover` 가 `_Profile_Render` 측점 끌기 한 곳에만 있어
  [직선화]·[쉬프트]·틸팅·방향키가 그냥 지나감. 판정 로직을
  `_Profile_MinCover.blocksMinCover()` 로 옮기고 모든 편집이 지나는
  `_Profile_Panel.applyEdits` 한 곳에서만 호출. 화면 모듈의 사본·죽은 배선
  (`enforceMinCover` ctx 필드, 미사용 import)은 제거.
- 되돌리기 범위: 이력이 `b05:`·`b05-`·`b06:`·`b06-` 세션 키만 스냅샷하는데
  구조물(관) 위치는 세션 밖이라 undo 가 계획선만 되돌림. `_Profile_Panel_Tools`
  가 기록 직전에 관 목록을 `b05:pipes` 로 흘리고 복원 뒤 되읽어 반영.
  사용자 확정 ⑥ 「범위는 B05 페이지 조작 전부」 충족.

검증(공용 브라우저, 용화 검증 프로젝트 route_id 126):
- 최소고 강제 켠 상태로 관 측점 264.060 방향키 ↓ → 편집 미적용 + 경고
  "배수관 Ø800 — 최소 계획고(지반 +1.3m) 아래로 내려갈 수 없습니다."
- 구조물 측점 264.060 → 263.960 이동 후 ↶ 1회 → 264.060 복귀,
  `b05:pipes` 목록도 264.06 으로 복귀.
- `tsc --noEmit` 0, prettier 통과, pytest 366 passed / 17 skipped / 0 failed.
- 파일 줄수: Panel 699 · Panel_Tools 197 · Render 350 · MinCover 188 (700줄 제한 준수).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 20:34:18 +09:00
eomsangdon 73a77a1376 Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1 2026-09-02 19:32:16 +09:00
eomsangdonandClaude Opus 5 3651ad9875 feat(B05): 종단 계획선 전체 측점 폴리라인 전환 + 직선화·쉬프트·undo/redo 신설
2026-09-02 사용자 지시 9건 반영. 화면 실조작 검증은 다음 세션 몫 (사용자 지시로
코딩까지만 진행).

1. 초기 계획선 = 전체 측점 폴리라인 — `design_ground_following_profile()` 신설.
   모든 측점을 변화점으로 잡고 계획고 = 원지반고, 라운드는 R을 지정한 자리에만
   (`build_curves(only_explicit=)`·`build_alignment(only_explicit_curves=)`).
   basis `ground_polyline`. 관 정착 선형은 폴백으로 내림.
2. 구간 쉬프트(⬆⬇) 삭제.
3. [직선화] 신설 — `B05_Profile_UI_Profile_Straighten.ts`. 두 측점의 라운드에 탄젠트한
   직선으로 대체하고 사이 라운드 삭제. 직선 틸팅 시 가운데 라운드 + 양측 탄젠트 재구성.
4. [쉬프트] 신설 — 직선 구간을 기하에서 되읽어(`detectStraightRun`) 복수 선택,
   최외곽 라운드 중심 기준 상·하 평행이동.
5. 방향키 조작 — 상하 0.1m 계획고, 좌우 0.1m 누가거리(구조물·비정규 측점 한정).
6. undo/redo 신설 — B05·B06 조작 세션 키 묶음 스냅샷(`_Profile_History.ts`).
   버튼은 요약줄 맨 앞(최대 기울기 좌측), 21x17px. Ctrl+Z / Ctrl+Shift+Z.
7. 종단 요약줄의 횡단배수 최소고 표시 삭제(산식·편집 차단은 유지).
8. [편집 되돌리기] 버튼 삭제 — undo/redo로 대체.
9. 지형 구분 기본값 특수지형 — 패널 셀렉트와 백엔드 기본값(스키마·체인 폴백) 일치.

700줄 제한 — 패널을 `_Profile_Panel_Tools` · `_Profile_Preview` 로 분리하고 측점↔구조물
짝짓기를 `_Profile_Structures` 로 이관(842줄 → 696줄).

검증: tsc --noEmit 오류 0, ruff format/check 통과,
pytest tmp/tests/ -q → 366 passed / 14 skipped / 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:30:16 +09:00
eomsangdon 5e51343f16 Merge remote-tracking branch 'origin/sub_laptop_1' into sub_desktop_1 2026-09-02 19:25:15 +09:00
eomsangdonandClaude Opus 5 ac983cf0e1 feat(B02): 등록 폼에 표제란 8칸 반영
사용자 지시(2026-09-02) — 프로젝트 등록에서도 수정 모달과 같은 항목 수신.

- 시행청·연도기번·사업량·설계일자·과업책임자·분야별책임자·설계자·회사 로고 8칸 추가.
  담당자는 select + 「신규 등록…」(관리자만), 로고는 회사 대표 로고가 기본값
- 담당자·자산 회사 경계 검사를 check_project_refs(company_id, data) 로 옮겨 B01 수정과 공용
- GET /admin/members 문턱을 require_company 로 — 일반 사용자도 담당자 선택 필요.
  _scope_company 가 남의 회사를 막고 쓰기는 관리자 그대로
- fix: road_type 스키마가 옛 값(branch·stream)을 받고 화면 선택지 work 를 막아
  「작업임도」 등록이 422 로 떨어지던 것 수정 — ^(main|fire|work)$
- 검증(5174 실조작): 등록 칸 14개로 수정 모달과 동일, 등록된 행에 8칸 그대로 저장
  (road_type=work, logo_asset_id=1, designer=3), 해당 프로젝트 표제란 12칸 전부 채워짐.
  pytest 151 passed (신규 test_b02_register_schema.py 8건)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:21:27 +09:00
eomsangdonandClaude Opus 5 3d0215f8d8 feat(B07): 표지에 회사 로고 자리 신설
- `00_template_cover.json` 에 Image 자리 추가 — `{{회사로고}}`,
  x 57.40~137.40 · y 64.06~104.06 (폭 80 · 높이 40)
- 자리는 오른쪽 아래 `{{시행청}}`(기준점 784.35, 84.06)과 좌우로 마주 보게 잡음.
  왼쪽 여백은 `{{연도기번}}`(x 57.4)과 맞추고 세로는 시행청 글자줄에 맞춤
- 값 공급은 도각과 같은 통로 — `build_cover_drawing` 이 이미 `_fill_placeholders` 를
  태우므로 로고를 못 구하면 종전대로 엔티티째 빠짐

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:19:57 +09:00
eomsangdonandClaude Opus 5 6e0c77d811 docs(B07): 94883733 내용 설명 — 도각 표제란 서명 자리 2개 신설
`94883733` 이 자동 폴백 커밋(`auto: …`)으로 묶여 나가 내용 설명이 빠짐. 이력 재작성
금지(CLAUDE.md 6장)라 amend 대신 이 빈 커밋으로 남김. 그 커밋의 실제 변경:

- `resources/template_2dDrawing/00_template_A1.json` — Image 자리 2개 추가.
  `{{과업책임자서명}}`(x 536.89~578.89) · `{{분야별책임자서명}}`(x 587.91~629.91),
  둘 다 y 17.5~25.5. 설계자 서명과 같은 폭 42·높이 8이고, 표제란 세로선
  (531.87 / 583.91 / 633.91) 이 만드는 칸 안에서 좌우 여백 4로 가운데 배치.
- `B07_DesignDetail_Router.py` `_title_block_fields` — 두 자리 값을
  `pm_user_id`·`field_lead_user_id` 계정의 `company_assets` SIGNATURE 자산에서 읽음
  (설계자와 같은 최신 1건 서브쿼리). 서명을 안 올린 사람 자리는 종전대로 엔티티째 빠짐.

자체검증: `frame-template` 응답이 Image 4건(회사로고+서명 3)으로 자리·좌표 확인,
`tmp/tests/test_b07_title_block_signatures.py` 3건 신설, `pytest tmp/tests/ -q` 15 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:09:43 +09:00
eomsangdon 94883733d5 auto: 2026-09-02 19:08 (EOMSANGDON-HOME) 2026-09-02 19:08:55 +09:00
eomsangdon f0dd4b08ef Merge remote-tracking branch 'origin/sub_desktop_1' into sub_laptop_1 2026-09-02 19:02:52 +09:00
eomsangdon aff882c2a0 Merge remote-tracking branch 'origin/main_desktop_1' into sub_laptop_1 2026-09-02 19:02:51 +09:00
eomsangdonandClaude Opus 5 dde40101bb fix(B07): 표제란 로고·서명 원천을 계정·회사 자산으로 이관
- 서명: `projects.signature_asset_id` 대신 설계자(`designer_user_id`, 없으면 소유자)
  계정의 `company_assets` SIGNATURE 자산을 따라감 (여러 벌이면 최신 1건)
- 로고: 프로젝트가 고른 것 우선, 없으면 `companies.logo_asset_id` (014) 로 폴백
- `projects.signature_asset_id` 는 컬럼만 남기고 더 읽지 않음 — B01 이 항상 null 저장

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:01:56 +09:00
eomsangdon a91262f110 Merge remote-tracking branch 'origin/sub_laptop_1' into sub_desktop_1 2026-09-02 18:57:57 +09:00
eomsangdon 85a478dd8a Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1 2026-09-02 18:57:56 +09:00
eomsangdonandClaude Opus 5 3a36af9a2c feat(B01): 서명은 사람 계정에, 회사 로고는 회사 등록 단계로 재배치
사용자 확정(2026-09-02) — 이름이 들어가는 자리는 서명도 받고, 담당자 선택에 신규 등록
항목을 두며, 회사 로고는 회사 등록 단계에서 받아 이후 변경.

- 014_company_logo.sql: companies.logo_asset_id 추가 (서명은 company_assets.user_id 로
  이미 표현되어 컬럼 없음). 공유 DB 적용 완료
- 사용자 수정 모달에 「서명 (도면 표제란)」 칸 — createAssetField 에 owner(주인 못박기)·
  onChange(즉시 반영) 추가로 재사용, 주인 고정 시 물리기 체크 잠금
- 담당자 select 3개에 「+ 신규 등록…」 — 계정 생성 모달 뒤 세 select 에 항목 삽입·자동 선택
- POST /admin/members 가 이름·직위·부서 수신, 계정 없으면 status=PENDING 으로 생성
  (로그인 불가 비밀번호). 700줄 제한으로 B01_Dashboard_Repository_Members.py 분리
- 회사 등록 모달에 로고 파일 칸, PUT /company/logo 신설, 회사 패널·회사 목록에서 변경
- 프로젝트 수정 모달의 「설계자 서명」 칸 제거 — signature_asset_id 는 null 로 고정 저장
- 검증(5174 실조작): 신규 등록 구성원 2→3(id 6 PENDING)·select 자동 선택, 서명 자산
  [4,'검증신규 서명',user_id 6] 생성, 회사 logo_asset_id None→1, 검증 자산·계정 정리.
  pytest 143 passed, tsc·ruff·prettier 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 18:57:16 +09:00
eomsangdonandClaude Opus 5 65d6fd261f fix(B07): 도각 편집 띠 사이드바 이관·깨진 그림에 렌더 루프 정지 해소
- 편집 띠를 CAD 위 절대배치에서 도면 목록 하단 액션 칸으로 옮김 —
  1행 편집 띠, 2행 [도각 편집]·[현재 도면 확정] (리본과 겹쳐 문구·버튼이 찌그러지던 문제)
- `drawImage` 가 'broken' 상태 그림에서 던지던 예외로 렌더 루프가 끊겨
  이후 모든 도면이 백지로 남던 결함 해소 — 못 읽은 그림은 건너뜀
- `ImageEntity` 가 자리표시자(`{{회사로고}}`)를 절대 URL 로 굳히던 저장 경로 수정 —
  원본 주소를 보관해 그대로 돌려주고, 자리표시자는 요청조차 하지 않음
- 버튼 줌(ZOOMIN·ZOOMOUT)이 배율만 바꿔 도면이 화면 밖으로 밀리던 것을
  화면 중심 고정으로 수정

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 18:12:28 +09:00
eomsangdonandClaude Opus 5 5fc8009925 feat(B01): 서명 그리기 캔버스 추가
- 자산 모달 서명 칸에 마우스 그리기 판 신설 — pointer 이벤트 + setPointerCapture,
  getBoundingClientRect 로 CSS 크기 → 캔버스 픽셀 환산, [지우기] 1개
- 올릴 때 canvas.toBlob('image/png') → signature.png 로 기존 업로드 통로
  (POST /company/assets) 재사용 — 백엔드·DB 변경 없음
- 파일을 안 고르면 그린 서명을 대신 실어 보냄, 로고 칸에는 캔버스 없음
- 검증(5174 공용 브라우저): 좌표 환산 실측 x 98–381 · y 38–111(요청 100–380 · 40–110),
  [지우기] 후 잉크 0, 저장 PNG 480×160 RGBA 5,250 bytes,
  프로젝트 signature_asset_id 2→3 반영 확인 후 원복. pytest 143 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 18:04:44 +09:00
eomsangdonandClaude Opus 5 2e57d29156 fix(B05,B03): 초기 설계 실패 시 초기값 부재 처리 — 실패 마커·안내 메일·[초기화] 거부
증상: 파일 입력 직후 초기 계산값과 B05 [초기화] 결과가 다름.

원인
- 자동설계 체인이 5단계까지 전부 성공해야만 `save_initial_snapshot()` 호출.
  중간에 깨지면 `initial_snapshot/` 미생성 → [초기화]가 복원 대신 재계산 폴백.
- 재계산 폴백의 지표면 기준이 체인과 다름 — 체인은 stage 1 확정값
  (`get_surface_confirmation_params`), [초기화]는 config 기본값
  (`surface_confirmation_defaults`). 실측: 프로젝트 stage1 `classification`/5m 대
  config `csf`/1m.
- 체인이 깨져도 WF1 은 초기 분석 완료 메일을 그대로 발송.

수정 (2026-09-02 사용자 확정 — 부분 결과는 분석 안 됨과 다르지 않으므로 부분
스냅샷은 만들지 않음)
- `initial_design.failed` 마커 신설 — 프로젝트 루트(스냅샷 4트리 밖), 실패 사유 기록.
  체인 진입 시 옛 마커 제거, 실패 5지점 + 스냅샷 저장 실패에서 기록.
- WF1 이 마커를 읽어 완료 메일 대신 `send_initial_design_failed_email()` 발송
  (관리자 주소 `ADMIN_EMAIL`, 없으면 주소 없이 안내).
- B05 [초기화]: 스냅샷 있으면 복원, 실패 마커 있으면 409 `initial_design_failed`
  로 거부(DELETE 앞에서 반환 — 데이터 무변경), 옛 프로젝트만 종전 재계산 폴백.
- 재계산 폴백의 지표면 기준을 `get_surface_confirmation_params()` 로 통일.

자체검증: `pytest tmp/tests/ -q` 366 passed / 14 skipped / 0 failed (+4).
공용 브라우저 실측 — 실패 프로젝트 [초기화] 409 응답·`routes` 행 무변경 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 18:03:18 +09:00
eomsangdonandClaude Opus 5 2bdad0947b fix(B07): 명령행 토글 기본값 숨김으로 변경
- 상태막대 `명령행` 토글의 초기 상태를 `useState(true)` → `useState(false)` 로 변경
- 진입 시 `--cad-command-height` 가 `0px` 로 잡혀 캔버스 세로 공간 확보
- 토글 동작·명령행 구현 자체는 불변

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 17:48:10 +09:00