feat(B03,B04,B05): 초기 설계가 도는 동안 B05·B06 진입을 막는다
파일을 올리면 지표면 확정 직후 "노선 설계로 이동하세요"를 적고 그 뒤에 초기 설계 체인이 돈다. 그 사이 B05에 들어가 만지면 반쯤 계산된 값을 편집하게 되고, 그 편집이 섞인 채 초기값 스냅샷이 찍힌다. 계산이 끝날 때까지 준비 화면(B11)에서 기다리게 한다. - initial_snapshot: 마커(initial_design.lock) 생성·해제·조회와 스냅샷 무효화, 편집 정본 제거를 한곳에 둔다. 마커는 스냅샷 4트리 밖(프로젝트 루트)이라 복원에 딸려 들어가지 않는다. - 체인 두 곳(자동·재설계): 시작에 마커, finally에서 해제. 재설계 체인은 끝에 스냅샷을 무효화한다 — 사용자 입력을 유지한 채 재계산하므로 그 결과를 찍으면 가짜 초기값이 된다. 다음 [초기화]가 폴백을 타며 새 초기값을 만들고 그때 촬영한다. - /surface/status: 마커가 있으면 in_progress + initial_design으로 답한다. stage 1이 COMPLETE면 진행률 파일을 무시하는 기존 분기를 건드리지 않으려고 별도 확인이다. - 진입 게이트(routeAfterPreloadCheck)와 B11이 그 상태를 보고 기다린다. 상태 조회가 실패하면 막지 않고, B11 대기는 20분 상한을 둔다. - [초기화] 폴백: 체인 전에 structures.json과 drainage/edits를 지운다. 남기면 구조물 측점이 사용자 편집분에서 다시 파생돼 초기값이 오염된다(2026-08-29 실측). 테스트 4건 추가. 249 passed·8 failed(기슭막이 이관 때 생긴 기존 실패, 변경 전 동일).
This commit is contained in:
@@ -143,12 +143,17 @@ async def run_auto_design_chain(
|
||||
from B05_Profile.B05_Profile_Router import confirm_latest_route, solve_route
|
||||
from B05_Profile.B05_Profile_Schema import RoutePoint, RouteSolveRequest
|
||||
from B06_Section.B06_Section_Router_Confirm import confirm_sections
|
||||
from common_util.common_util_initial_snapshot import save_initial_snapshot
|
||||
from common_util.common_util_initial_snapshot import (
|
||||
clear_designing,
|
||||
mark_designing,
|
||||
save_initial_snapshot,
|
||||
)
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from common_util.common_util_surface_confirmation import surface_confirmation_defaults
|
||||
from config.config_db import get_db_pool
|
||||
|
||||
pool = get_db_pool()
|
||||
project_root: Path | None = None
|
||||
try:
|
||||
# 1) 수동 이력 보호 — 경로가 이미 있으면(사용자 작업 또는 이전 자동 실행) 건너뛴다.
|
||||
async with pool.acquire() as connection:
|
||||
@@ -164,6 +169,9 @@ async def run_auto_design_chain(
|
||||
|
||||
# 2) 계획노선 CSV → BP/EP/경유점. 없으면 자동 경로를 세울 근거가 없다.
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
# 이 체인이 끝나기 전에는 B05·B06에 들어가면 안 된다 — 반쯤 계산된 화면을 만지면
|
||||
# 그 편집이 섞인 채 초기값이 찍힌다(2026-08-29 사용자 확정, CLAUDE.md 5장).
|
||||
mark_designing(project_root)
|
||||
points = _planned_route_points_in_project_crs(project_root)
|
||||
if not points:
|
||||
logger.warning("자동 설계 체인 중단(계획노선 CSV 없음): project_id=%s", project_id)
|
||||
@@ -249,6 +257,10 @@ async def run_auto_design_chain(
|
||||
except Exception:
|
||||
# 체인은 업로드·WF1 흐름의 부가 작업이다 — 어떤 예외도 밖으로 던지지 않는다.
|
||||
logger.exception("자동 설계 체인 실패: project_id=%s", project_id)
|
||||
finally:
|
||||
# 성공·실패·중단 어느 쪽이든 문은 연다 — 마커가 남으면 영영 못 들어간다.
|
||||
if project_root is not None:
|
||||
clear_designing(project_root)
|
||||
|
||||
|
||||
async def run_redesign_chain(
|
||||
@@ -280,14 +292,29 @@ async def run_redesign_chain(
|
||||
)
|
||||
from B06_Section.B06_Section_Router_Confirm import confirm_sections
|
||||
from B06_Section.B06_Section_Schema import SectionConfirmRequest
|
||||
from common_util.common_util_initial_snapshot import (
|
||||
clear_designing,
|
||||
discard_initial_snapshot,
|
||||
mark_designing,
|
||||
)
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from common_util.common_util_workflow_state import get_workflow_state
|
||||
from config.config_db import get_db_pool
|
||||
|
||||
_ = get_project_storage_relative_path # 시그니처 정렬용 (사용 안 함)
|
||||
pool = get_db_pool()
|
||||
project_root: Path | None = None
|
||||
try:
|
||||
async with pool.acquire() as connection:
|
||||
latest = await get_latest_route(connection, project_id)
|
||||
stored_path = await get_project_storage_relative_path(connection, project_id)
|
||||
if stored_path:
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
mark_designing(project_root)
|
||||
# 지표면이 바뀌면 옛 초기값은 다른 지형 기준이라 더는 기준이 아니다. 여기서
|
||||
# 다시 뜨지는 않는다 — 이 체인은 **사용자 입력을 유지한 채** 재계산하므로 그
|
||||
# 결과를 찍으면 편집이 섞인 가짜 초기값이 된다(2026-08-29 사용자 확정).
|
||||
if discard_initial_snapshot(project_root):
|
||||
logger.info("재확정 체인: 초기값 스냅샷 무효화 (project_id=%s)", project_id)
|
||||
if not latest:
|
||||
logger.info(
|
||||
"재확정 체인 → 기존 경로 없음, 신규 자동 체인으로: project_id=%s", project_id
|
||||
@@ -433,3 +460,6 @@ async def run_redesign_chain(
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("재확정 체인 실패: project_id=%s", project_id)
|
||||
finally:
|
||||
if project_root is not None:
|
||||
clear_designing(project_root)
|
||||
|
||||
Reference in New Issue
Block a user