재생성이 `cross_sections` 행을 지우고 다시 쓰면 `data.design` 에 면적 4키만 남는다.
`get_cross_section_designs` 는 `ground_type` 있는 줄만 담으므로 그 상태는 「설계 없음」이고,
B08 토적표가 통째로 0 이 됐다(창 셋에서 같은 증상).
원인은 결함이 아니라 분담이었다 — 체인은 재생성 뒤 [확정]을 불러 그 자리를 채웠고,
재생성만 따로 도는 길에는 그 단계가 없었다. 그래서 [확정] 없이도 정본이 서게 한다.
- [저장]이 확정과 **같은 조회**(`get_cross_sections_missing_design_chainages`)로
빈 측점을 골라 기본 설계로 채운다.
- ⚠ **값이 있는 행은 안 건드린다** — 사용자 조작값·이월분이 거기 있다. 시험으로 잠금.
- 실측(route 184): 저장 뒤 DB 설계 62행·38키 복구, 층따기 13,696.8 ㎡,
측구 토사 0.173 / 암 8.886 ㎡ — 파일 기반 값과 일치.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
새 사용자 값을 더할 때 **네 곳을 다 고쳐야** 했고, 하나만 빠지면 그 값이 저장에서
조용히 사라졌음(`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>
`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>
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>
측점마다 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 저녁 사용자 확정. 이유는 속도가 아니라 보안 — 저장 경로가 브라우저 계산이면
유토 배분·운반거리 코드(_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>
`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>
포장·세월교 보정 헬퍼를 저장 시점으로 옮기면서 `@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): 조작은 캐시에 쌓이고 [저장]·[확정]에서만 영구저장소에
나간다. 카드 버튼(지반유형·단면유형·측구·포장·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): 조작 중과 [저장]·[확정]의 계산은 브라우저 몫이고,
서버는 초기값을 만들 때만 같은 코드를 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>
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>
@
다단은 소유 벽 연장에 종속이라 길이·전/후를 따로 못 잡았다. 계곡부·능선부에서는
아래 단일수록 연장이 달라져 자동 규칙으로 못 잡으므로 수동 입력으로 간다.
조정창 UI는 기준벽 것을 그대로 쓴다(길이·기준측점 전·후 세 행).
- Const: EXTRA_SPAN_DEFAULT(10m·5/5) + applySpanPatch()로 길이↔전/후 산식을
기준벽과 공용화
- Station_Controls: StructureSpanControl에 tierValuesFor/updateTier 추가. 값은
세션(b06:extraspan)에 담고 캐시(design.extra_spans)에도 얹는다. 단 수를 줄이면
그 단의 구간값도 지운다
- View_Structure: 다단 키면 단별 제어기로, 기준벽은 종전 경로
- Wire: culvertReach에 단별 연장을 더하고, 링크 카드에서 각 단을 자기 구간값으로
판정한다(tierSpanOf)
- Corridor_Structures: 3D 스윕 범위를 단별 구간값으로
- 저장: CrossSectionPatch.extra_spans(프론트 payload + WallSpanPatch 스키마 +
확정 병합) — [저장]·[확정] 때 정본에 남는다
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
기슭막이 범위(길이·기준측점 전/후)를 B06 횡단도 조정창에서 지정하게 하고, 그
범위가 덮는 옆 측점의 기슭막이를 "연동" 파생물로 다루도록 체계를 세웠다.
값의 원천은 배수관 옵션(pipe_points) 하나다 — 같은 숫자를 B06 정본에 따로 담으면
B05 배수관 카드와 갈려 어느 쪽이 참인지 알 수 없다.
① 집수정 전/후 필드 신설 — inlet_basin_before_m·after_m(기본 각 1m), 길이 2m 유지.
종방향 구간 계산을 revetSpanOfSpec·basinSpanOfSpec 한 곳으로 모아 2D 링크 판정과
3D 스윕이 같은 값을 쓰게 했다.
② B06 조정창에 길이·전·후 행 — 기슭막이(유입·유출)·집수정 모두. 캐시를 고쳐 즉시
보여주고 저장은 조작이 멎으면 한 번만 나간다(세부유역 재계산이 매번 돌지 않게).
길이를 바꾸면 늘어난 몫만 지금 비율대로 나눠 담는다 — 총길이에서 비율로 다시
계산하면 0.1m 반올림이 쌓여 5.0/5.0이 5.6/5.4로 어긋난다.
③ 링크 카드 선택 허용 — "소유 측점 통째 복사"에서 "위치 4축만 물려받고 원지반
접합은 이 측점 지형으로 재계산"으로 바꿨다. 구조물 형식은 언제나 소유 측점 값이다.
손대기 전에도 어긋나지 않게 소유 측점이 실제로 적용한 4축(revetShift)을 명시값으로
물려준다.
④ 연동(측점별)·경사(전체 공통) 토글 — 연동 조작은 소유 측점 키에 실리고 그 연장이
덮는 측점을 함께 다시 그린다. 경사는 3D 전용 스위치다(끄면 스윕 프레임 dz=0).
횡단도까지 -dz로 밀면 노견에서 출발하는 성토선까지 내려가 노견과 벌어진다.
⑤ 가림 구간 선 정리 — 유입부 접속선과 유출부 접지 후 절토선을 2D·3D 모두에서
빼되 기하는 남긴다. 면적·수량 계산은 건드리지 않았다(전면 개편 대상).
⑥ 집수정은 소유 측점 횡단도 하나에만 — 링크 카드에서 집수정·계류측 다단을 뺀다.
⑦ 3D 구간 겹침 정리 — 측점 간격이 구조물 연장보다 좁으면 두 구간 제어점이 번갈아
서서 짧은 쪽(집수정 2m)이 잘려 보였다. 두 소유 측점의 중간에서 끊는다.
⑧ 조정창 정리 — 십자·9키를 맨 아래로, 창을 위쪽 기준 오버레이로(낮은 횡단도에서
제목이 잘리던 문제), 연동·경사를 1행 토글 버튼으로, 스크롤 자리 유지.
파일 분리: B06_Section_UI_Cross_View_Structure.ts(조정창 배선),
B06_Section_Api_Culvert_Options.ts(구간값 저장) — 700줄 제한.
BUILD_VERSION 14 → 17(저장 코리도 만료).
tsc/ruff/prettier 통과, pytest 196 passed(기존 실패 1건 유지).
작업이력·자체검증 기록: docs/raw/PLAN.md §4-9.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3D 예상형상은 종단·횡단 확정 뒤의 최종 산출물이고 3D 쪽 편집 경로는 없다
(2026-08-24 사용자 확정). 그런데 구조물(비정규) 측점은 횡단 정본 자체가 없어
조회할 때마다 프리뷰 기본값이 재계산됐고, 조정창 조작값은 세션에서 사라졌다.
정본 경로를 열고, 그 위에서 코리도 단차를 걷어냈다.
① 구조물 측점 정본화 — 확정·임시저장이 종단 정본 stations 중 DB 행이 없는
측점을 대상에 넣어 upsert. cross_sections 19 → 23행.
② 조정창 조작값 정본화 — 기슭막이 4축(x·d·h·m)·다단 단 수를 design.revet_adjust
/ extra_wall_counts로 저장하고, 3D culvertLayoutOf가 그 값을 기하 입력으로 쓴다.
카드 우선순위는 세션 → 정본 → 자동.
③ 노폭 연장의 노견 편입 — PIECE_COLS.shoulder 2→3으로 원 노견 끝 꺾임을 남기고,
조기 return을 없애 확폭이 구조물 구간 내내 유지되게 했다.
④ 종단 기울기 반영 — 구조물 구간 복사 단면에 계획고 차이를 전량 얹는다.
노견 표고 단차 0.49~0.61m 제거.
⑤ 접속 테이퍼 — 구간 경계를 제어점으로 세워 "복사 vs 보간" 두 규칙을 하나로
통합. 노폭 3.0m·성토비탈 8.1m 단차 제거. 접속 길이 = 인접 측점까지.
제어점 생성은 B05_Profile_UI_Corridor_Frames.ts로 분리(Build 699 → 635줄).
⑥ 인접 측점 링크 — 기슭막이 연장(기본 10m)이 덮는 옆 측점 횡단도에도 소유 측점
단면을 계획고 차이만 얹어 그린다. 요소별 연장 판정(관·집수정 제외), 표시 전용.
BUILD_VERSION 11 → 14 (저장 코리도 만료).
tsc/ruff/prettier 통과, pytest 192 passed (기존 실패 1건 유지).
작업이력·자체검증 기록: docs/raw/PLAN.md §4-8.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
자동 체인 상태 전이 분리:
- confirm_latest_route/confirm_sections에 mark_stage_complete 플래그 추가
- 자동 체인(신규·재확정)은 데이터만 확정하고 stage 2·3을 IN_PROGRESS로 남김
(스텝바 기존 노란 테두리 = 계산됨·확정 전 상태로 재사용)
- B06 [확정]이 stage 2+3 동시 완료 + 경로 상태 CONFIRMED 보강 후 B07 수량 이동
B05 좌측 패널 개편:
- 포인트 팔레트 + 임도 기준·옵션을 경로 계산 설정 한 컨테이너로 병합,
[최적 경로 계산] 버튼을 컨테이너 내부로 이동 (가끔 쓰는 무거운 재계산)
- 하단 액션 행 [초기화][임시저장][횡단 이동]으로 교체, 경로 단독 확정 폐지
- [임시저장] = 관로 + 계획선 델타 + 비정규 측점·상단측 저장(mark_stage_complete=false)
- [횡단 이동]/B06 [종단 이동] = 저장 없이 페이지 이동만 (B05·B06 캐시 공유 구조 유지)
- [초기화] = POST /route/reset 신설: 기존 경로 삭제 후 계획노선 CSV 기본값으로
자동 체인 재실행(파일입력 직후 상태 복원), 분석용 타임아웃 적용
B06 액션 행: [종단 이동][임시저장][확정] 3버튼 구성, locale 키 정비
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>