feat(clinical): 정렬이 안 끝나도 임상 측정으로 넘어갈 수 있게

실제 인체에서 정렬 로직이 끝까지 안 맞을 수 있다. 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>
This commit is contained in:
2026-09-09 13:55:52 +09:00
parent 2853127ff5
commit d66b69506f
7 changed files with 334 additions and 26 deletions
+25 -1
View File
@@ -67,6 +67,27 @@
| `repeats_saved` | int | 실제로 저장된 반복 수 |
| `source_file` | string | 원본 CSV 파일명 |
| `avg`, `delay_us`, `samples` | number | 측정 파라미터(매니페스트에 있을 때만) |
| `anchor_cm` | int / null | 정렬로 정한 부착 위치(치골 위 cm) |
| `anchor_basis` | string / null | 그 위치를 **누가** 정했나 — 아래 |
#### `anchor_basis` — 정확도 분석 전에 반드시 보셔야 하는 값
정렬 알고리즘은 실제 인체에서 끝까지 안 맞을 수 있습니다(CH3 가 어느 위치에서도 안
잡히거나, 탐색 상한까지 가도 종료 조건이 안 걸림). 그때 진행을 막으면 환자를 눕혀 둔
채 임상이 멈추므로, **간호사가 현재 위치로 그냥 진행할 수 있게** 열어 두었습니다.
그래서 부착 위치에는 세 가지 출처가 섞여 있습니다:
| 값 | 뜻 | 정확도 분석 |
|---|---|---|
| `algorithm` | 알고리즘이 best 를 확정 (`action == STOP`) | **모집단에 포함** |
| `reattach_override` | 알고리즘은 재부착 권고, 그대로 진행 | 제외 |
| `manual` | 탐색이 끝나기 전에 사람이 이 위치로 결정 | 제외 |
| `null` | 정렬을 거치지 않고 측정 | 제외 |
"기존 초음파 측정기 대비 정확도" 를 낼 때 **`algorithm` 만 추려야** 합니다. 나머지를
섞으면 사람이 고른 자리의 오차가 알고리즘의 오차로 계산됩니다. 세션 목록에서 이 값을
필터로 걸어 주시면 그 실수를 막을 수 있습니다.
### `records[]`
@@ -151,6 +172,8 @@
| `selection_verified_against_reference` | bool | 앱 판정이 Python 레퍼런스와 대조 완료인가 |
| `positions` | array | 위치별 지표 — 아래 |
| `lateral` | object | 좌우 정렬 결과 — 아래 |
| `anchor_basis` | string / null | 이 위치로 임상을 진행한 근거. 600 의 같은 필드와 같은 값입니다. **null = 정렬만 하고 임상으로 넘어가지 않음** |
| `proceed_anchor_cm` | int / null | 실제로 임상에 넘긴 cm. `action != STOP` 인 채 진행하면 `anchor_cm` 은 null 인데 이 값은 들어 있습니다 |
> `params` 는 `align_result.json` 을 **통째로** 옮깁니다(`patient` → `subject`,
> `save_name` 만 이름이 바뀝니다). 앱에서 요약에 필드를 추가하면 별도 작업 없이
@@ -174,7 +197,8 @@
| 필드 | 설명 |
|---|---|
| `done` | 좌우 확인 단계를 거쳤는가. **false 는 "안 맞췄다"가 아니라 "확인 안 했다"** |
| `done` | 좌우 판정값(commit)이 있는가 |
| `confirmed` | 간호사가 "좌우 확인 완료"를 눌렀는가. **`done` 과 다른 사실입니다** — 좌우를 맞추는 중에 임상으로 넘어가면 `done=true, confirmed=false` 가 됩니다 |
| `lat_tol` | 허용 오차 (8) |
| `accum_k` | 판정에 쓴 프레임 수 (10) |
| `action` | `STOP` / `MOVE_LEFT` / `MOVE_RIGHT` / `PROBE_LR` / `MOVE_UP` |