Commit Graph
14 Commits
Author SHA1 Message Date
eomsangdonandClaude Opus 5 b1593bce83 feat(B07): 표제란에 로고·서명 자리 신설 + 시행청·담당자 DB 확장
사용자 지시(2026-09-02) — 로고·서명을 임의로 만들어 표제란에 넣고, 위치·크기는
도각을 고치면 따라오게 하며, 시행청·과업책임자 등은 DB를 넓혀 채울 것.

도각(표제란)
- `00_template_A1.json` 에 그림 자리 2개 추가 — 회사 로고는 용역회사 칸 왼쪽
  (316~348 × 20~36mm), 설계자 서명은 설계 칸 아래(638~680 × 17.5~25.5mm).
  자리·크기가 **템플릿 좌표로만** 정해지므로 도각을 고치면 그대로 따라옴.
- `_fill_placeholders` 가 그림 자리(`imageData`)의 `{{키}}` 도 채움. 값을 못 구하면
  빈 문자열을 남기지 않고 **엔티티째 제거** — 빈 값은 CAD 가 깨진 그림으로 그림.
- `_transform_entity` 가 `points` 배열도 옮김. 그림(로고·서명)과 띠(Hatch)가 이 키를
  쓰는데 여태 변환 대상이 아니라 도각을 옮기면 제자리에 남았음.

DB (`011_title_block.sql`, 전부 ADD COLUMN·NULL 허용)
- `projects` — `client_org`(시행청), `pm_user_id`·`field_lead_user_id`·`designer_user_id`
- `companies.logo_path` · `users.signature_path` — 그림은 파일로 두고 경로만 담음
  (기존 `storage_path`·`input_files` 와 같은 방식)
- FK 는 걸지 않음 — 사유를 파일 머리말에 적음(소프트 삭제·LEFT JOIN·4환경 공유).

배선
- `_title_block_fields` 가 시행청·과업책임자·분야별책임자·설계자(배정 없으면 소유자)와
  로고·서명 data URL 까지 실어 보냄.
- `read_stored_asset()` 신설 — `storage/` 기준 상대 경로의 **파일**을 읽음.
  `resolve_stored_project_path()` 는 폴더를 만드는 프로젝트 루트용이라 파일에 못 씀.

CAD 결함 1건 (이번 작업에서 드러남)
- `screenCanvas.drawController.drawImage` 가 `(width, height)` 를 좌표처럼 변환해
  화면 오프셋과 y 뒤집기가 섞여 들어갔음 — 그림이 제 자리를 벗어나고 비율이 무너짐.
  크기는 배율만 곱하고 자리는 세계 중심으로 잡도록 고침(SVG 컨트롤러와 같은 방식).
- 검증 창구 `__aisloCad.screen(x, y)` 추가 — 세계→화면 좌표. 그림·글자가 제 자리에
  그려졌는지 픽셀로 판정할 때 씀.

검증: `pytest tmp/tests/ -q` 135 passed / 0 failed(그림 자리 3건 신규),
`npx vitest run` 87 passed / 0 failed, `check-types`·`build` 통과.
화면 실측(5174, wdw): 표준도 API Text 24개 `{{` 잔존 0, Image 2개가 도각 좌표
(316,20)-(348,36)·(638,18)-(680,26)에 실림. 캔버스 픽셀 판정 — 두 자리 모두 배경색
외 픽셀이 그려짐(로고 88px·서명 64px), 로고 파랑(20,70,140)이 슬롯 안에서만 검출.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:35:25 +09:00
eomsangdonandClaude Opus 5 793e1aad37 fix(B07): 표제란 값 공급 통로 + CAD 회귀 6건 해소
표제란(도각) 값 공급
- `use_title_fields()` 문맥 변수 신설 — 회사 도각 폴더와 같은 방식이라 작도 엔진
  6개의 서명을 고치지 않음. `frame_entities` 가 문맥값 위에 호출부 값(도면명)을 얹음.
- 라우터가 도면 한 건을 그리기 전에 DB 값을 세움: 공사명·위치(projects),
  용역회사(companies), 설계자(users). 못 채운 자리는 빈칸 — 도각 원본의 남의 값이
  도면으로 나가지 않음.
- 미공급 항목과 사유를 `_title_block_fields` 주석에 명시: 시행청·과업책임자·
  분야별책임자는 DB 칸 부재(B02 마이그레이션 대기), 축척·사업량·연도기번은 임의 수치
  금지, 설계일자는 채울 시점 정의 미결.

CAD 회귀 6건 (87건 중 81 passed → 87 passed)
- `window` 부재 5건: `notifyWindow()` 한 곳으로 모으고 `window` 없는 Node 시험에서는
  통지를 건너뜀. `triggerReactUpdate` 가 이미 같은 이유로 시험을 건너뛰던 것과 같은 결.
- `find-closest-entity`: 목의 호가 90°까지만 돌아 클릭점에서 147px 떨어져 있었음 —
  시험 의도(호가 가장 가깝다)대로 3/4바퀴로 되돌림.
- 선색 기대값 4건: 앱 기본 선색이 흰 종이 배경에 맞춰 검정으로 바뀐 뒤 상류 기대값
  `#fff` 가 남아 있었음 — `getActiveLineColor()` 대조로 교체.
- 좌표 NaN: 없어진 `TOOLBAR_WIDTH` 를 시험 보조가 아직 참조 — 입력 컨트롤러가 캔버스
  bounding rect 를 쓰도록 바뀐 현행에 맞춤.
- 호 각도 비교: 같은 각의 음수·2π 표기 차이라 [0, 2π) 정규화 후 대조.

검증: `npx vitest run` 87 passed / 0 failed, `npm run check-types`·`npm run build` 통과,
`pytest tmp/tests/ -q` 131 passed / 0 failed(신규 `test_b07_title_block_fields.py` 4건 포함).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:00:39 +09:00
eomsangdonandClaude Opus 5 4cb9b15939 style: 저장소 전체 포맷터 일괄 적용 (prettier·biome·ruff)
파일마다 포맷 폭이 달라(≈80 대 100) 한 줄만 고쳐도 포맷터가 무관한 줄을 대량
재포맷했음. 사용자 지시로 전체를 한 번에 맞춤. 코드 동작 변경 없음 — 포맷만.

- 프론트엔드 `.ts/.css/.html` → 저장소 prettier (`.prettierrc`, printWidth 100)
- `B07_DesignDetail/openwebcad/**` → 자체 biome (tab 들여쓰기·single quote·lineWidth 100).
  `biome format` 만 사용 — `biome lint --write` 는 포맷 아닌 코드 수정까지 하므로 제외
- 파이썬 → `ruff format` (엔진 코드는 이미 정합, resources·scratch 스크립트 24개만 변경)

두 포맷터가 서로 되돌리지 않도록 `.prettierignore` 신규 — openwebcad 와 빌드·산출물
폴더를 prettier 대상에서 뺌. `.prettierrc` 에 `endOfLine: "auto"` 추가 — 기본값 `lf` 가
`core.autocrlf=true` 로 받은 CRLF 파일을 매번 전부 다시 써서 `--list-different` 가
실제 포맷 차이를 가리고 있었음.

검증: `tsc --noEmit` 통과(루트·openwebcad 둘 다), pytest 349 passed / 17 skipped /
0 failed, CAD vitest 87건 중 81 passed / 6 failed(laptop-sub 기준선과 동일, 회귀 없음).
포맷터 재실행 시 prettier·biome 모두 변경 0건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 07:08:24 +09:00
eomsangdonandClaude Opus 5 946d61f8df feat(B07): GROUP 묶음 JSON 저장·복원, 줌 로그 제거
- `groupId` 를 도면 JSON 에 실어 [저장] 뒤 다시 연 도면에서도 묶음 유지.
  엔티티 13종의 `toJson` 대신 직렬화 한 곳(export/import)에서 붙이고,
  복원은 순번이 아니라 id 로 — `compact` 가 null 을 걷어 index 가 어긋남.
- `setScreenScale` 의 매 호출 `console.log` 제거 — 줌·팬마다 콘솔을 덮었음.
- 검증: `group-id-roundtrip.test.ts` 신규, `npx vitest run` 87건 중 81 passed,
  실패 6건은 작업 전과 동일한 기존 결함. `npm run build` 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 06:33:19 +09:00
eomsangdonandClaude Fable 5 17e14a25ed fix(B07): 편집이 사라지지 않게 하고 확정한 도면을 잠근다
사용자 편의성 점검 15건 중 반영 확정분 12건.

편집 손실 방지
- CAD에 미저장 표시(drawingDirty)를 두고, 도면을 바꾸기 전에 묻는다.
  전에는 그은 선이 경고 없이 사라졌다(실측: 캔버스 서명이 원본과 동일).
- 도면 전환을 겹쳐 눌러도 늦게 온 응답이 화면을 덮지 않게 가드를 뒀다.
- 도면층 삭제·객체 이동을 되돌리기에 실어 Ctrl+Z로 돌아오게 했다.

확정한 도면은 읽기 전용
- runCommand 한 곳에서 보는 명령만 통과시킨다(허용 명시 방식).
- 도면을 실을 때 앞 도면의 그리기 도구를 내린다 — 켜 둔 도구가 확정본에도
  계속 그렸다.
- 확정 버튼 자리를 [현재 도면 확정] / [수정]으로 가른다. 확정 해제는 [수정]
  한 곳뿐 — 되돌리기·색 고르기로 확정이 풀리던 경로를 없앴다.

그 밖
- 잠금 도면층(도각·등고선·계류)은 마우스가 스쳐도 하이라이트하지 않는다.
- 준비 중 도면 7종도 빈 도각으로 열린다(눌리지 않는 회색 버튼 제거).
- 도면층 행이 잘리지 않게 패널을 300px로 넓히고 이름을 줄여 담는다.
- 기본 그리기 색을 검정으로 — 흰 종이에 흰 선이라 안 보였다.
- 색 고르는 동안 변경 통지가 연발하지 않는다.
- 수량표는 앞 단계 산출물이라 도면에서 고치지 못하게 막고 안내한다.
- 자동백업 칸을 도면별로 나눈다 — 되살리면 다른 도면에 붙었다.
- 횡단 장 id를 시작 측점 기준으로 바꿔 구간이 달라져도 옛 확정이 안 붙는다.
- 회사 도각 저장 전에 좌표를 검사하고, 기본 도각으로 되돌리기를 연다.
- 횡단 파일이 없어도 나머지 도면 목록은 남는다.
- 불러오는 중·실패를 화면에 표시한다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 19:05:22 +09:00
eomsangdonandClaude Opus 5 64d944eb0f fix(B07): 도각을 배경으로 굳혀 새 선이 잠금 도면층에 떨어지지 않게 한다
- state.setActiveLayerId(): 잠금 도면층(도각·원지반) 요청은 잠금 아닌 첫 층으로
  돌린다. 브리지·자동백업 복구·JSON 가져오기·도면층 패널·리본이 모두 이 함수를
  지나므로 가드를 한 곳에 뒀다. 잠금 아닌 층이 없으면 요청값 유지.
- RibbonWidgets: 「현재 도면층」 목록에서 잠금층을 뺀다(현재 층이 잠금이면 유지).
- Cover 엔진: 표지에 잠금 아닌 「주기」 도면층을 실어 덧그릴 자리를 만든다.
  표지는 엔티티가 전부 도각(잠금)이라 가드만으로는 그릴 곳이 없었다.
- state.resetUndoBaseline(): 도면 적재를 되돌리기 바닥으로 세운다. 첫 Ctrl+Z가
  도면을 싣기 전 빈 상태로 되돌아가 화면이 백지가 되던 결함을 닫는다.
- debug-hook: 검증용 snap() 창구 추가. state.test.ts 3건 신규.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:58:08 +09:00
eomsangdonandClaude Opus 5 6a2c5d9f7b feat(B07): 기본 도면층 이름을 국문으로 바꾸고 방위표 템플릿을 정리한다
도면층 17종을 프로젝트가 이미 쓰는 용어로 맞춘다 — 원지반·계획선·구조물·
암 경계선·수량 산출표·도각·등고선·계류·노선·배수유역·유역 제원·표제·
그래프 격자·측점표·유토곡선·그래프 축·운반 구간. CAD 앱의 'Default'와
'New layer'도 기본·새 도면층으로 바꾼다. layerId는 그대로다.

방위표 템플릿(00_template_compass.json)에 원본 DXF의 표제란 글자 4개가
도면 반대편에 남아 있었다. 이 때문에 템플릿 bbox가 183x262mm로 부풀어,
긴 변을 14mm에 맞추는 compass_entities()가 방위표를 1.1x1.8mm로 줄여
놓고 있었다. 글자를 지우니 bbox 20.5x32.9mm, 방위표가 8.7x14mm로 나온다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:07:14 +09:00
eomsangdonandClaude Opus 5 9c1cdf8f4d perf(B07): 도면을 미리 받아 두고, 커서 지연과 토스트 이질감을 걷는다
도면을 열 때마다 0.8~1.0초가 걸렸다. 서버가 요청마다 도면을 새로 조립하고(0.3~0.6초),
구조물 모듈이 공유 캐시를 건너뛰고 종횡단 상세를 매번 다시 받았기 때문이다(0.2초).
진입 후 첫 장을 띄우고 나서 목록 전체를 배경에서 받아 캐시에 담고, 구조물 모듈은
B05·B06과 같은 Section_Store를 쓴다. 클릭에서 렌더까지 55~65ms로 줄었다.

CAD 알림은 react-toastify라 프로젝트 공용 토스트와 모양이 달랐다. 호출부 30여 곳을
그대로 두려고 vite alias로 라이브러리 자리에 다리를 끼워, 부모가 있으면 메시지로 넘겨
부모 토스트로 띄운다. 백업 되살리기처럼 누르는 안내를 위해 showToast에 onClick을 더했다.

커서가 미묘하게 끌리던 것은 프레임이 느려서가 아니라(16.7ms, 60fps) 합성 파이프라인
때문이다. 화면 캔버스를 desynchronized+alpha:false로 만들어 그 큐를 건너뛴다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 21:05:23 +09:00
eomsangdonandClaude Opus 5 8c828992f7 fix(B07): 집기 반경을 화면 기준으로 잡고, 누른 채 끄는 선택을 살린다
- 집기 반경이 월드 거리였다. HIGHLIGHT_ENTITY_DISTANCE를 월드 거리와 그대로 비교해
  확대할수록 화면상 반경이 같이 커졌다(1369%에서 15월드 ≈ 205px). helpers/pick-radius로
  확대율을 나눠 쓰고 상수를 화면 픽셀 10으로 낮췄다. 하이라이트·선택·그립·시퀀스 도구가
  같은 반경을 본다.
- 누른 채 끄는 선택이 없었다. 왼쪽 버튼은 mouseup에서만 클릭을 보내 누르고 끌어도 점이
  하나만 찍혔고, 그래서 좌→우(창)·우→좌(교차) 구분이 먹히지 않는 것처럼 보였다.
  선택 도구일 때만 누를 때 첫 점을 보내고, 4px 넘게 끌고 놓으면 그 자리로 사각형을 닫는다.
  다른 도구는 예전처럼 놓을 때 한 점만 받는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 15:52:25 +09:00
eomsangdonandClaude Opus 5 ea5ce92725 feat(B07): 표를 객체로 만들어 도면의 표가 진짜 표가 되게 한다
표가 DXF의 표로 나가야 한다(사용자 확정). 지금까지 도면의 표는 선과 문자 뭉치라
내보낼 때 고를 수 있는 길이 하나뿐이었다.

- TableEntity: 열별 폭·행별 높이·칸 문자·병합을 한 객체가 들고 있다. 격자선은 담지
  않고 병합 자리에서 선을 끊는 규칙을 표가 스스로 안다(helpers/table-geometry.ts).
  회전·대칭은 지원하지 않는다 — 표는 축에 붙어 있다.
- 명령: TABLE을 표 객체 생성으로 다시 쓰고 TABLEEDIT(칸 문자)·TABLEROW·TABLECOL·
  TABLEMERGE·TABLEUNMERGE를 더했다. EXPLODE는 표를 선과 문자로 흩는다.
- 그립: 좌측 상단으로 표를 옮기고, 열·행 경계로 폭·높이를 바꾼다.
- 백엔드: 유역 정보표와 횡단 수량 산출표를 표 객체로 낸다. 횡단표는 머리행이 폭
  8등분, 본문이 11열 가중치로 격자가 서로 달라 두 경계를 합친 18열로 만들고 병합으로
  원래 칸을 되살렸다 — 손으로 하던 가로선 끊기가 사라졌다.
- 수량 역추출: 값 Text의 결정적 id로 읽던 것을 칸에 실은 key로 읽도록 옮겼다. 이미
  저장된 도면을 위해 옛 방식을 폴백으로 남겼다.

토적도·종단표는 값이 칸이 아니라 측점 위치에 놓이는 성격이라 이관하지 않았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 15:12:19 +09:00
eomsangdonandClaude Opus 5 e599884888 feat(B07): 그립으로 고치고, 음수·극좌표를 받고, 편집분을 잃지 않는다
조사표 8~13절 검토에서 "기본 기능"으로 고른 것을 반영한다.

- 좌표 입력: 절대·상대 좌표가 양수만 받아 `@-100,50`을 거부했다. 부호를 허용하고
  극좌표 `@거리<각도`·`거리<각도`를 더했다. `-`·`+`가 확대·축소 단축키로 먼저
  잡혀 음수의 첫 글자를 먹고 있어 그 두 단축키를 뺐다(줌은 휠·뷰 막대·명령).
- 그립 편집: 선택 객체에 그립을 그리고 집어서 옮긴다. 선 끝점·중점, 폴리선 정점,
  사각형 모서리, 원 중심·반지름, 문자·점 기준점. 폴리선은 세그먼트 중점을 끌면
  정점이 늘고 정점 위 Ctrl+클릭이면 준다. 형상 필드가 private이라 공개 생성자로
  다시 만들어 바꿔 끼우고 id·도면층·색·그룹을 물려받는다.
- 자동 백업·복구: 5초 디바운스로 복구 전용 키에 저장하고, 시작할 때 백업이 있으면
  눌러서 되살리는 안내를 띄운다. 저장(QSAVE)에 성공하면 백업을 지운다.
- 선택 순환: 같은 자리를 다시 클릭하면 겹친 후보를 차례로 돌린다.
- 상태막대: `극좌표 추적` 버튼이 `직교`와 같은 onClick이라 같은 일을 하고 있었다.
  각각 45°·90°를 켜고 끄도록 고치고, 스냅 추적 토글과 F3·F7·F8·F10을 붙였다.
- 문자 굵게·기울임을 캔버스·SVG·JSON·스타일 패널에 연결했다.
- 조사표: 이미 되어 있던 5건의 표기를 정정하고, 출력·내보내기(9절)는 결재창 이후
  PDF·DXF·DWG로 반영할 것이라 보류(P)로 구분해 사유를 남겼다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 14:43:46 +09:00
eomsangdonandClaude Opus 5 3fccf8278b feat(B07): 그린 것을 블록으로 저장해 다시 놓고, 사진 넣기를 되살린다
조사표 4·7절 검토 결과를 반영한다.

- 블록 라이브러리: 선택한 객체를 이름 붙여 저장(BLOCK)하고 팔레트에서 골라
  클릭 한 번으로 놓는다(INSERT). 정의-참조 링크 없는 클론 방식이라 새 엔티티
  타입 없이 기존 직렬화·groupId를 그대로 쓴다. 기준점은 선택 영역 중심.
  라이브러리는 도면과 분리해 브라우저에 두고, 유실 대비로 파일 내보내기·
  가져오기를 붙였다.
- 사진 넣기: IMAGEATTACH가 파일 선택창을 띄우지 않아 도구가 FILE_SELECTED를
  영원히 기다리고 있었다. 도구형 명령도 run()을 함께 부르게 하고 파일 선택을
  붙여 되살렸다.
- 조사표 4·7절의 반영 등급·반영 열을 실제 코드에 맞춰 정정하고, 반영하지
  않기로 한 항목의 사유를 절마다 남겼다. 7절은 신규 구현 없음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 14:00:42 +09:00
eomsangdon ea9d9685be feat(B07): CAD를 AutoCAD 명령 체계로 재구성한다 (조사표 1·2·3·5절)
저장소 사고로 잃은 4개 커밋(38eb7cd4·b8a11756·281528bf·f60b488d)의 작업물을
하나로 다시 담았다. 내용은 동일하다.

■ 구조
- commands/: 명령 정의(id·AutoCAD 별칭·글리프·도구/즉시실행)를 단일 소스로 두고
  리본·명령행·단축키가 모두 이 레지스트리를 읽는다. tools/tool.consts.ts 폐지.
- ribbon/: 탭→패널→명령 데이터(ribbon.config.ts)와 범용 렌더러 분리.
  AutoCAD 배치(홈·삽입·주석·뷰·출력)와 패널 확장(▾)을 따른다.
- tools/factories/sequence-tool.ts: 점·숫자·문자·객체·선택 단계를 선언하면
  xstate 머신을 만들어 주는 공장. 명령당 170줄 보일러플레이트 제거.
- helpers/geometry/: 3점 호·정다각형·타원·스플라인·구름형·평행이동·해치 스캔선·
  점렬 샘플링 등 상태 없는 순수 함수.
- Toolbar 483줄을 QuickAccessBar/Ribbon/InspectorPanel/StatusBar/CommandLine/
  ViewControls/PropertiesEditor/QuickProperties로 분해.

■ 명령 (조사표 기준)
- 1절 그리기 21건, 2절 수정 27건, 3절 도면층·특성·그룹·유틸리티 39건 전부 반영.
- 5절 주석 34건 중 26건 반영(문자·치수 16종·지시선·표·구름형·주석 축척).
- HatchEntity 추가(solid·pattern·cross·gradient) + JSON 왕복, Layer에 색·선가중치·
  선종류·동결·투명도 필드 추가, 그리기 루프가 동결·숨김·투명도를 반영.

■ 화면 실측에서 고친 결함
- 명령행 포커스 상태에서 ENTER가 도구로 가지 않던 문제
- 명령행 문자 입력이 접두사가 같은 명령으로 실행되던 문제
- 명령이 끝나도 입력을 계속 먹던 문제(점 입력 명령만 반복, 나머지는 선택 도구 복귀)
- 시퀀스 단계 인덱스 오사용 9건 + 단계 값 종류 검사 추가
- 해치 내부를 클릭해도 선택되지 않던 문제

미반영 8건(맞춤법 검사·꺾기 치수·치수 끊기/재연관/검사 치수·기하공차·지시선 수집·
축척 리스트 편집)은 조사표 `반영` 열과 PLAN.md에 사유를 적었다.
2026-08-30 01:34:13 +09:00
eomsangdonandClaude Fable 5 6e195afb69 feat(B07,B08): 워크플로 순서 교환 — 상세설계를 수량산출 앞으로
횡단설계(B06) 다음을 상세설계 → 수량산출 → 설계도서 순으로 재배열하고,
폴더 번호가 흐름과 일치하도록 이름을 맞바꾼다.

- B08_DesignDetail → B07_DesignDetail, B07_Quantity → B08_Quantity
  (파일 접두어·식별자·라우트·locale 키 전량 스왑)
- STAGE_KEYS 4=DESIGN_DETAIL, 5=QUANTITY 스왑 + 라우터 stage 리터럴 교체
- CAD 마운트 /b08-cad → /b07-cad (main.py·vite proxy·iframe URL),
  openwebcad Toolbar 라벨 B07로 수정 후 재빌드
- 유지: openwebcad postMessage 프로토콜 aislo:b08:*·패키지명(내부 식별자)
- 기존 프로젝트 storage 폴더 rename + project_manifest 갱신,
  DB project_workflow_stages stage_no 4↔5 행 스왑 완료

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:16:46 +09:00