임상팀 결정(2026-10-01, e.yu · kai.han): 모든 자세에서 정렬을 하므로 "best + 1cm 올리고
거기서 좌우" 단계를 빼고, 확인 측정한 best 자리에서 바로 좌우를 맞추고 그대로 붙인다.
· AnchorConfig.FINAL_OFFSET_CM = 1 → 0. anchor_cm = best_cm. 2026-10-01 이전 세션
(final_offset_cm: 1)과 대조할 때는 Python select_supine_anchor(offset_cm=0).
· 지시문: 확인 측정 뒤 "치골 위 N cm 그대로 — 좌우 맞추기 / 옮기지 말고". 오프셋이
0 이 아닐 때의 "N+offset cm 에 붙이세요" 는 남겨 둔다(상수만 되돌리면 그대로 동작).
· AnchorGuide STOP 문구도 "이 자리(N cm)에 그대로 부착합니다".
· 재부착 확인의 기준(좌우 확인 창)은 이제 확인 측정과 같은 높이 — "떼기 직전" 이라 그대로 둔다.
시험 AlignInstructionTest 오프셋 0 문구 1건 추가 — 전체 192건 · failures 0.
문서: CLINICAL_ALGORITHM §3.5/3.6/상수표, LABDB_DATATYPES final_offset_cm/anchor_cm, README.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
35°~55° 는 Supine 에서 프로브가 0° 라는 가정이다. 배가 나온 환자는 누워서도 20° 쯤
나오고, 그러면 Sitting 최적 구간은 55°~75° 다(2026-10-01 제안).
· Supine 정렬의 확인 측정(N cm, 20 cycle)에서 IMU 평균 기울기 m 을 재
AppState.supineTiltBaseline 에 두고, 요약 align_result.json 에 confirm_tilt_deg_mean 으로
남긴다(어느 자세든). 앱을 다시 켜면 그날 Supine 정렬 요약에서 되살린다.
· PostureTilt.rangeFor/status 가 기준각을 받아 Sitting 범위를 (35+m)..(55+m) 로 민다.
기준각이 없으면 절대 각도 그대로.
· 카드: Sitting 에서 "IMU 기울기 n° · Supine 대비 +k°", 띠는 밀린 범위, 문구
"Sitting 측정 가능 각도 55°~75° (Supine 20° + 35~55)". 기준각이 없으면 "Supine 기준각
없음" 을 적는다. 다른 자세에서는 "Supine 기준각 20° 저장됨 (Sitting 범위 55°~75°)".
· run 매니페스트: tilt_deg_required 는 밀린 값, tilt_baseline_supine 추가.
· 환자가 바뀌면 기준각도 비운다.
시험 PostureTiltTest 기준각 1건 추가 — 전체 191건 · failures 0.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
임상 프로토콜은 자세마다 정렬을 처음부터 다시 한다(2026-10-01 확인): Supine 정렬 →
Supine 0~100% → Sitting 정렬 → Sitting 0~100% …. 앱은 환자당 한 번이었다 — Sitting 에서
다시 정렬하면 같은 `align/` 의 원시 파일이 덮이고 labdb 세션 ID 도 같아 섞였다.
· AppState: 부착 위치·근거·기준 채널·재부착 결과를 자세별 맵(AnchorState)으로. 기존
접근자(anchorCm 등)는 지금 자세의 항목을 읽고 쓴다. 환자·프로브가 바뀌면 전 자세를
비운다(clearAllAnchors).
· 저장: `align/` → `align_{자세}/`. 업로드 대상 탐색은 옛 `align` 과 `align_*` 전부.
labdb 601 세션 이름에 자세 꼬리(`…_align_sitting_HHmm`), 요약에 `posture`.
· 정렬 화면: 자세 칩은 첫 위치를 재고 나면 잠근다(폴더·세션의 조건이다). 바꾸려면
[처음부터 다시 정렬].
· 병원 화면: 환자명 → 자세 → 정렬 카드 순(자세가 정렬보다 위). 카드가 그 자세의 상태를
보인다 — "Sitting 부착 위치 미정렬" / "✓ Sitting 부착 위치 3cm 확정".
시험: 자세별 폴더 대상 탐색 · 세션 이름 자세 꼬리. 전체 190건 · failures 0.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
임상에서 정렬 → 위치 마킹 → 프로브 떼기 → 크래들로 다시 붙이기 사이에 CH2 가 사라진 채
18조합을 다 쟀다. 좌우는 실시간 판정만 하고 아무것도 남기지 않아 "그 전에는 있었나"를
대조할 수도 없었다. 세 가지를 넣는다.
1. 재부착 확인 (병원 화면 정렬 카드 아래, ReattachCheckCard)
· 부착 위치에서 정렬 조건(2.3MHz·c3·20 cycle)으로 재고, 정렬 확인 측정에서 잡힌
채널(AppState.anchorRefChannels ← 확인 측정의 walls)과 CH0~3 을 채널별로 대조
+ 탈착 감시. 판정은 ReattachCheck.judge (순수 함수, 시험 6건).
· 막지 않는다 — 측정 시작 아래 경고, 측정 중 한 줄 요약에 "재부착 ⚠", run 매니페스트
`anchor_reattach`, align_result.json `reattach`, 601 에 phase=reattach 로 파형·IMU.
· AppState.clearAnchor() — 부착 위치와 딸린 것(근거·기준 채널·재부착)을 한꺼번에 비운다.
2. 좌우 스트리밍 전체 기록 (HospitalRunStore.LateralStreamWriter)
· 프레임마다 6채널 + 그 프레임의 판정(u4·u5·imbalance·action·ch3)을
align_{n}cm_lateral_stream.csv 에 덧붙여 쓴다(앱이 죽어도 그때까지 남음).
`attempt` 열로 [처음부터 다시 정렬] 시도를 가른다 — 앞 시도의 비동기 업로드가 파일을
읽고 있을 수 있어 지우지 않는다. 601 에 phase=lateral_stream + `lateral{…}`.
3. 정렬 화면 파형 1차 개편 — 파형을 용적·접촉보다 위에 더 크게(170dp), 그 위에 채널별
벽 검출 띠(CH0~3 ✓/✗, CH4·5 는 좌우용 회색). "CH2 없음"이 파형보다 먼저 보인다.
시험 88건 통과(전체 174 · skipped 3 · failures 0). 실기(프로브 필요)는 다음 임상에서.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
좌우 정렬은 연속 스트리밍이라 그동안 아무것도 저장하지 않았다. "맞췄다"고 확인한 그
자리의 파형·IMU 가 파일에 없으니 나중에 좌우 판정을 대조할 길이 없었다(2026-09-30 요청).
· 좌우 루프가 마지막 프레임 창(LateralGuide.required = 10, 판정 1회에 쓰는 수)을
파형+IMU 짝으로 들고 있다가, [좌우 확인 완료]에서 부착 위치 cm 으로
`align_{n}cm_lateral.csv` · `align_{n}cm_lateral_imu.csv` 를 쓴다.
다시 시작·처음부터 다시 정렬 때는 창을 비운다.
· HospitalRunStore: 파일 종류를 AlignFileKind(SWEEP/CONFIRM/LATERAL) 로 —
`confirm: Boolean` 대신. 같은 cm 이라도 종류가 다르면 다른 파일이다.
· AlignLabdbPayload: `_lateral` 을 phase=lateral 로 올린다(탐색 → 확인 → 좌우 순).
positions_measured 는 sweep 만, confirm_positions 는 confirm 만, lateral_frames 추가.
미업로드 대상 판정(raw = sweep)은 그대로.
· 문서(LABDB_DATATYPES.md) 601 phase·params·IMU 파일명 갱신.
시험: HospitalRunStoreAlignFilesTest 4건 신규, 페이로드 2건·대상 판정 1건 추가 — 80건 통과.
실기(프로브 필요)는 다음 임상에서 확인.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 임상: 조합 CSV(600)는 올라갔는데 정렬(601)이 안 올라갔다. 폰 없이 코드에서
정렬만 다르게 취급되는 지점을 찾았다.
1. 정렬 testId 에 날짜가 없었다 — `<환자명>_align`. labdb 는 같은 testId+rowIndex 를
조용히 무시하므로(labdb.md "자동 무시"), 같은 환자·같은 시험 이름을 다른 날 다시
정렬하면 그날 정렬은 한 건도 안 들어가는데 응답은 200 이라 앱은 성공 마커를 남긴다.
다른 기기가 먼저 쓴 이름이면 409. 조합 CSV 는 파일명(날짜·자세·충만도·주파수)이
testId 라 이 문제가 없었다. → `<날짜_환자폴더>_align_<HHmm>`. 시각은 정렬 화면의
시작 시각을 align_result.json `started_at` 으로 남겨 쓴다. [처음부터 다시 정렬] 은
앞 시도를 먼저 올리고 새 시각을 받는다 — 같은 날 두 번 정렬해도 세션이 갈린다.
2. `*_imu.csv` 가 미업로드 대상에 들어갔다 — 조합 CSV 의 IMU 형제 파일인데 세션으로
올리려다 변환 실패 마커만 남기고 "미업로드 N건"을 부풀렸다(A34 실측 2건). 제외.
3. 정렬은 정렬 화면을 떠나는 순간 한 번만 올리고 재시도가 없었다. 그 순간 망이 막혀
있으면 정렬만 빠진다. → 임상 실행이 끝날 때마다 그 폴더의 미업로드 정렬을 한 번 더 민다.
4. onDispose 가 첫 컴포지션의 saveName 을 붙들었다 — 정렬 화면에서 환자명을 나중에
치면 측정은 환자 폴더에, 업로드는 `unnamed_…` 빈 폴더를 보고 끝났다. → rememberUpdatedState.
시험: sessionBase 4경우 · IMU 형제 제외. services.labdb 40건 통과.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
labdb 에서 CSV 로 받으면 ax..gz 열이 빈칸이고 raw JSON 에는 IMU 가 있다(2026-09-29
보고). 사이트의 파싱 문제가 아니다 — labdb 표준(labdb.md "000" 레코드)은
`sensor.imu` 가 샘플 하나 `{ax,ay,az,gx,gy,gz}` 이고 CSV 내보내기는 그 여섯 값을
편다. 병원 임상(600)·정렬(601)은 2026-09-21 부터 거기에 회차 샘플 전부(배열)를
넣고 있었다. 사내 임상(001)은 처음부터 표준대로 보내고 있었다.
· LabdbSensor.of(samples): imu = 마지막 샘플 하나(표준·CSV 에 나옴),
imu_samples = 전부(FIFO 순), imu_sample_count. 없으면 빈 객체(0 으로 안 채움).
001 의 LabdbUploader·tools/labdb_upload.py 와 같은 모양.
· HospitalLabdbPayload·AlignLabdbPayload 가 이것을 쓴다.
· tools/align_analyze.py 는 imu_samples 를 우선 읽고, 09-21~28 분(imu 가 배열)도
그대로 받는다.
· docs/LABDB_DATATYPES.md §IMU 를 새 모양으로. 09-21~28 업로드분은 CSV 에서
IMU 가 비는 이유와, 내보내기에서 배열이면 마지막 원소를 쓰는 선택 사항을 적었다.
시험: LabdbSensorTest 3건 신규, Align/Hospital 페이로드 시험을 새 모양으로
(services.labdb 38건 통과). 전체 168건 중 실패 3건은 옛 세션 임시 경로에 쓰는
덤프 시험(Cm3PerTraceDump·GoldenDump·PrecisionDump)으로 이 변경과 무관하다.
병원 폰(SM-A155N)에 설치 완료.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
병원 경로는 `mtb?` 를 보내면서 **piezo 콜백만** 걸고 있었다. 응답에 딸려 오는
`rim:`(IMU)은 받는 곳이 없어 그대로 버려졌고, 저장된 측정에 IMU 가 하나도 없었다.
labdb 600/601 의 `sensor` 가 "항상 빈 객체"였던 이유다.
## 왜 필요한가
자세는 조작자가 고르는 실험 조건이라 판정에는 안 쓴다. 필요한 것은 다른 것이다 —
같은 조건 20회 반복에서 **어떤 회차만 값이 튈 때**, 알고리즘 문제인지 환자가 그 순간
움직인 것인지 가를 근거가 없었다. 정렬도 사람이 프로브를 옮겨 가며 재는 과정이라,
특정 위치의 파형이 이상할 때 자리 탓인지 흔들림 탓인지 알 수 없었다.
## 수집 — ImuSidecar
세 루프가 같은 패턴이라 헬퍼로 뺐다. 저장이 있는 두 곳에만 붙인다:
· HospitalModeView (600 · 20회 반복)
· AnchorAlignView (601 · 0~4cm 탐색 + 확인)
좌우 정렬 루프는 화면 안내용 스트리밍이라 저장이 없어 건드리지 않았다.
응답 순서가 `reb×6 → raa → rim` 이라 IMU 가 나중에 온다. piezo 를 받은 뒤 0.7초
기다리고, 안 오면 비운다. **IMU 가 없다고 측정을 실패로 돌리지 않는다** — 펌웨어·설정에
따라 `rim:` 이 없을 수 있고, 그때 실패로 만들면 기존에 되던 일이 안 되게 된다.
## 저장 — 형제 CSV
파형 행(meta + s0..s99)을 넓히지 않았다. 그 헤더는 이미 올라간 데이터와 파서가 함께
쓰는 규약이고, IMU 는 채널당이 아니라 **회차당** 값이라 같은 행에 넣으면 6 채널 행에
같은 IMU 를 여섯 번 복사하게 된다.
600 <측정파일>_imu.csv scan_id 로 파형과 잇는다
601 align_{n}cm_imu.csv
align_{n}cm_confirm_imu.csv 파형과 같은 confirm 분리 규칙
601 에서 confirm 을 따로 두는 이유는 파형과 같다 — 확인 측정의 IMU 가 판정에 쓰인
탐색 측정 것을 덮으면 안 된다.
## 업로드 — sensor.imu (600·601 같은 모양)
"sensor": { "imu": [ {ax,ay,az,gx,gy,gz}, … ] }
ax/ay/az = g · gx/gy/gz = dps
⚠ **없으면 빈 객체다. 0 으로 채우면 안 된다.** 부재가 곧 "그때는 안 쟀다" 이고,
0 으로 채우면 "무중력·완전 정지"로 정반대로 읽힌다. 이전 업로드분 전부가 여기 해당한다.
## 문서
LABDB_DATATYPES.md 의 "sensor 는 항상 빈 객체" 기술을 고치고 IMU 절을 새로 썼다.
labdb 쪽에 필요한 일(저장·뷰어 표시·없음/0 구분·마이그레이션 불필요)과 파생값
(accel_mag·gyro_mag) 계산식을 함께 적었다. 프로토콜 이름과 기존 필드는 그대로라
추가 키뿐이며 기존 파서를 깨지 않는다.
테스트 3건 추가(IMU 유/무 · cycle 분리 · confirm 이 sweep 을 덮지 않음).
601 15건 · 600 11건 전부 통과.
⚠ 실기기 미검증 — 병원 설정(2.3MHz·c3)에서도 `rim:` 이 오는지는 프로브로 확인해야 한다.
파싱 자체는 dev 임상에서 쓰던 같은 수집기다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## 화면을 두 덩어리로 나눴다
환자명 → 부착 위치 정렬 → 자세 → 방광 채움 → 반복 횟수 → 복부 두께 →
[측정 시작] → BV 카드
──────────────────────── 구분선 ────────────────────────
수동 확인 — 저장되지 않습니다
프로브 파라미터 주입 → [1회 측정]/[연속 측정] → BV → 마지막 측정 → 6채널
종전에는 BV 측정 구간이 [측정 시작] **위에** 있어 프로토콜 흐름 한가운데 끼어 있었다.
수동 측정 값은 저장도 업로드도 안 되는데, 한 흐름으로 붙어 있으면 조작자가 그것을
프로토콜의 한 단계로 착각한다. 구분선과 "저장되지 않습니다" 한 줄을 넣었다.
프로토콜 BV 카드는 실행이 끝난 뒤에도 남긴다(caption 이 "직전 회차"→"마지막 회차").
방광을 비우기 전에 방금 받은 값이 말이 되는지 한 번 더 볼 수 있어야 한다.
## 6조합 순회 → 복부 두께 2택
40mm 이하 1.8MHz c3 → n회
40mm 초과 1.8MHz c3 · 2.3MHz c3 → 2n회
cycle 은 3 고정. 한 단계 20회 × 6조합 = 120회가 환자를 너무 오래 눕힌다.
조작자에게 freq·cycle 을 고르게 하지 않는 것이 핵심이다. 눈으로 보고 답할 수 있는
것은 복부 두께이고, 조합은 거기서 따라 나온다(AbdomenThickness).
## 진행 판정: 개수 비교 → 집합 비교 (여기가 제일 위험했다)
`EXPECTED_COMBINATIONS = 6` 으로 **개수**를 세고 있었다. 그대로 두면 요구 조합이 1~2개인
지금 어떤 단계도 영원히 PARTIAL 로 남는다. 그런데 단순히 6을 2로 바꾸면 더 나쁘다 —
2.3MHz 를 두 번 채운 것과 1.8·2.3 을 각각 채운 것이 같은 숫자가 되어, 두꺼운 환자의
단계가 완료로 잡히고 **그 자리에서 1.8MHz 를 영원히 놓친다.**
`scanProgress(patient, day, expected)` 로 기대 집합을 받아 `containsAll` 로 본다.
expected 가 비면 완료라고 말하지 않는다 — `containsAll(emptySet)` 은 항상 true 라
가드가 없으면 아무것도 안 잰 단계까지 완료가 된다. 옛 6조합 데이터는 두 집합 모두의
상위집합이므로 계속 완료로 읽힌다(과거 데이터가 뒤집히면 간호사가 다 다시 잰다).
## 완료 칩을 초록 → 회색
초록은 "좋은 상태"로 읽혀 조작자가 거기서 멈춘다. 실제 의미는 "이미 받았으니 다음으로
가라"다. 아직 안 받은 칩이 눈에 들어와야 한다 — 남은 일이 어디인지가 그 줄의 존재
이유다. 누를 수는 있게 둔다(파형이 이상해 다시 재는 경우).
## 레퍼런스 알고리즘 스위치 제거
병원 임상의 값은 최종 보고본 하나여야 한다. 스위치가 있으면 화면을 본 사람과 기록을
읽는 사람이 서로 다른 경로의 숫자를 같은 값이라고 믿을 수 있고, 그 착오는 기록의
알고리즘 라벨을 일일이 확인해야만 드러난다. 두 경로 비교는 테스트가 맡는다
(ClinicalBvTest · AlgoPathReportTest).
## 기록
매니페스트에 `abdomen_thickness` / `abdomen_thickness_label`. 조합만 남기면 "왜 1개만
돌았나"를 나중에 답할 수 없다 — 시간이 없어 끊은 것과 얇아서 하나면 됐던 것이 구분되지
않는다. LABDB_DATATYPES.md 에 "한 조건에 세션 1개인 것이 정상"을 박았다.
테스트 114개 통과(진행 판정 12개 전면 재작성). HospitalProgressTest 가 과거 격자를
손으로 적지 않고 HOSPITAL_COMBINATIONS 를 참조한다 — 둘이 갈리면 "과거 데이터는 계속
완료로 읽힌다"는 보장이 거짓이 된다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
실제 인체에서 정렬 로직이 끝까지 안 맞을 수 있다. CH3 가 어느 위치에서도 안 잡히거나
(REATTACH), 상한까지 올라가도 nch 가 붕괴하지 않아 종료 조건이 안 걸린다. 그런데 진행
버튼이 두 군데서 막혀 있었다.
탐색 중(!done) 버튼이 **없다** — 측정 버튼만 있다
REATTACH 판정 버튼이 **비활성** (enabled = action == STOP)
후자가 특히 나쁘다. REATTACH 도 done 이라 2단계 화면으로 넘어가는데, 거기서 진행 버튼이
회색이고 "처음부터 다시 정렬" 밖에 없다. 환자는 누워 있고 방광은 계속 차는데 앱이
다음 화면을 안 내주는 막다른 길이다.
진행 버튼을 단계 밖으로 빼서 **항상 활성**으로 두었다. 막는 조건은 측정·좌우 스캔 중
뿐이다 — 그때 나가면 프로브 설정 복원이 끊겨 그 위치 데이터가 반쪽이 된다. 넘기는
위치는 `last?.anchorCm ?: cm`, 둘 다 "프로브가 지금 있는 자리"라 화면 숫자와 기록이
어긋나지 않는다.
**막지 않는 대신 근거를 같이 들고 간다.** AnchorBasis 를 새로 만들었다:
algorithm 알고리즘이 best 확정 (STOP)
reattach_override 재부착 권고를 무시하고 진행
manual 탐색 도중 사람이 결정
이게 이 커밋에서 제일 중요한 부분이다. 위치(cm)만 들고 가면 사람이 고른 자리와
알고리즘이 고른 자리가 기록에서 한 덩어리가 된다. 그러면 "기존 초음파 측정기 대비
정확도"를 낼 때 사람이 고른 자리의 오차가 알고리즘의 오차로 계산되고, 나중에 둘을
가를 단서가 없어 **데이터 전체가 주장을 받치지 못한다.**
그래서 근거를 세 곳에 남긴다: appState(화면), 600 매니페스트, 601 align_result.json
(`anchor_basis` · `proceed_anchor_cm`). LABDB_DATATYPES.md 에 "정확도 분석에는
algorithm 만 추려야 한다"를 표로 박았다 — 서버 쪽이 세션 목록에서 필터를 걸 수 있다.
화면도 근거에 따라 갈린다. 진행 버튼은 algorithm 일 때만 초록이고 나머지는 주황,
AnchorCard 도 같은 규칙이다. 같은 색으로 두면 간호사가 "정렬 끝났다"로 읽는다.
버튼 아래 한 줄로 무엇을 건너뛰는지 말한다(proceedNote) — 다 맞췄으면 아무 말도 안
한다. 늘 뜨는 경고는 곧 무시당한다.
lateral 요약에 `confirmed` 를 추가했다. 좌우를 맞추는 중에 넘어가면 commit 은 있어도
확인은 없는데, 기존 `done` 하나로는 그 둘이 구분되지 않아 확인한 세션으로 잘못 읽힌다.
테스트 110개 통과(신규 11). AnchorAction 이 늘어나면 조용히 MANUAL 로 떨어지므로
entries.size 를 고정해 그때 이 결정을 다시 보게 했다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
labdb 관리자가 코드를 배정했다(2026-09-09). 임시로 쓰던 사용자 정의 구간에서 옮긴다.
901 → 600 병원 임상 측정
902 → 601 부착 위치 정렬
서버 조사에서 나온 지적 셋을 함께 처리했다.
**① 적재량 오기 정정.** 문서·주석·테스트에 "279건(2026-09-05)"이라고 적혀 있었는데
틀렸다. 279 는 일반 앱의 **미업로드 세션 수**였고 병원 적재량과 무관하다 — 그걸
옮겨 적으면서 섞였다. 실제는 **72세션 · 1,440레코드**(2026-09-04)이고 서버 조회로
확인했다. 기존 적재분은 서버에서 이미 600 으로 마이그레이션됐다.
**② subject 가 서버에서 비어 있었다.** 서버는 최상위 `data.subject` 만 읽어
`sessions.subject` 컬럼에 넣는데 앱은 `params` 안에만 넣고 있었다. 그 컬럼이 export
파일명 prefix 와 목록 표시에 쓰이므로, 72세션 전부 환자 구분이 안 되는 상태였다.
두 페이로드 모두 최상위에 추가한다(params 안에도 그대로 둔다 — 분석 쪽이 이미 쓴다).
환자명이 비면 필드 자체를 넣지 않는다: 빈 문자열이 들어가면 목록에서 빈칸과
구분이 안 된다.
**③ 601 은 레코드 시각이 업로드 시각으로 채워진다.** 원본 정렬 파일에 시각 열이 없어
`datetime` 을 못 넣는데 서버의 `records.timestamp` 는 NOT NULL 이다. 동작에는 문제가
없지만 전 레코드가 거의 같은 시각이 되므로, **조회·시각화는 rowIndex 로 정렬해야 한다**
— KDoc 과 스펙 문서에 명시했다.
PC 변환기(align2labdb.py · hospital2labdb.py)도 같이 고쳤다. 기존 902 정렬 1세션은
같은 testId 로 재업로드해 601 로 갱신했다(600 72 · 601 1 로 확인).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
params 를 화이트리스트로 채우고 있었는데, 그 목록에 없던 두 필드가 조용히 빠져 있었다:
· patient_unnamed — 환자명 없이 잰 데이터인가
· selection_verified_against_reference — 앱 판정이 레퍼런스와 대조 완료인가
요약에 필드를 추가할 때마다 여기도 고쳐야 하는데 안 고쳐도 아무 신호가 없다. 이 세션은
"파형이 이상하니 봐 달라"는 진단 요청이라 **덜 보내는 쪽이 위험**하다 — 개발자가 되물어야
하고 그 왕복이 임상에서는 하루다.
align_result.json 을 통째로 옮기고, 이미 다른 이름으로 넣은 것(patient→subject,
save_name)만 건너뛴다. 요약의 모든 키가 params 에 도달하는지 테스트로 고정했다.
문서도 함께 고쳤다. positions[] 표에서 ch3_hit·ch3_tot·eligible 이 빠져 있었고
(실제로는 올라가고 있었다), params 표에 위 두 필드를 추가했다. 표에 없는 키가 보여도
정상이라는 설명도 넣었다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
labdb 서버에 새 dataType 두 개를 등록해야 한다. labdb.md §2-3 이 요구하는 항목
(코드 · 데이터 의미 · records[] 스키마 · 시각화 요구사항)을 그대로 채웠다.
지금은 사용자 정의 구간(900~999)에 임시로 올리고 있다. 901 로는 2026-09-05 수동
업로드분 279건이 이미 쌓여 있어, 코드를 바꾸면 마이그레이션이 따라야 한다는 점도
적었다.
스키마는 각 payload 파일 KDoc 에도 있지만, 서버 쪽에 들고 갈 때 코드를 열게 할 수는
없어 문서로 뽑았다. 두 곳이 갈라지지 않도록 정의 파일 경로를 문서 머리에 적어 뒀다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>