fix(labdb): 902 가 정렬 요약을 통째로 보내게 한다

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>
This commit is contained in:
2026-09-08 14:12:17 +09:00
parent 287296480c
commit afeb3acc57
3 changed files with 51 additions and 15 deletions
@@ -124,15 +124,18 @@ object AlignLabdbPayload {
put("save_name", saveName)
put("positions_measured", files.size)
put("cycles_total", records.length())
// 판정 근거를 통째로 넣는다. 개발자가 답해야 할 질문이 "왜 이 위치인가"라,
// 판정 근거를 **통째로** 넣는다. 개발자가 답해야 할 질문이 "왜 이 위치인가"라,
// 지표(nch·ch3·cap_frac)와 선택 결과가 파형과 같이 있어야 한다.
//
// 고를 키를 나열하지 않는다. 예전에 화이트리스트로 두었더니 `patient_unnamed`
// 과 `selection_verified_against_reference` 가 조용히 빠졌다 — 요약에 필드를
// 하나 추가할 때마다 여기도 고쳐야 하는데 그걸 알아챌 방법이 없다. 진단용
// 페이로드는 **덜 보내는 쪽이 위험**하다.
//
// 이미 다른 이름으로 넣은 것만 건너뛴다.
summary?.let { s ->
for (k in listOf(
"device", "firmware_version", "hw_preset", "freq_mhz", "freq_option",
"cycles_per_position", "probe_cycles", "avg", "delay_us", "samples",
"ch3_hit_min", "final_offset_cm", "best_cm", "anchor_cm", "action",
"stop_reason", "max_nch", "positions", "lateral",
)) if (s.has(k)) put(k, s.get(k))
val mapped = setOf("patient", "save_name") // → subject / save_name
for (k in s.keys()) if (k !in mapped) put(k, s.get(k))
}
// 자동이 아니라 사람이 올린 것임을 데이터에 남긴다 — 정상 세션과 섞이면
// "왜 이것만 올라와 있나"를 나중에 설명할 수 없다.
@@ -94,6 +94,30 @@ class AlignLabdbPayloadTest {
assertEquals("nurse_review", params.getString("upload_reason"))
}
@Test fun `요약의 모든 키가 params 로 간다`() {
// 고를 키를 나열하면 요약에 필드를 추가할 때마다 여기도 고쳐야 하는데, 안 고쳐도
// 아무 신호가 없어서 조용히 빠진다(실제로 patient_unnamed 와
// selection_verified_against_reference 가 그렇게 빠져 있었다).
// 진단용 페이로드는 덜 보내는 쪽이 위험하다.
val d = tmp.newFolder("align")
writeCycles(d, 0, cycles = 1)
val s = summary().apply {
put("patient_unnamed", false)
put("selection_verified_against_reference", true)
put("나중에_추가될_필드", "값")
}
File(d, "align_result.json").writeText(s.toString(), Charsets.UTF_8)
val params = AlignLabdbPayload.build(d)!!.getJSONObject("params")
for (k in s.keys()) {
if (k == "patient" || k == "save_name") continue // subject / save_name 으로 매핑
assertTrue("요약의 '$k' 가 params 에 없다", params.has(k))
}
assertEquals(false, params.getBoolean("patient_unnamed"))
assertEquals(true, params.getBoolean("selection_verified_against_reference"))
assertEquals("값", params.getString("나중에_추가될_필드"))
}
@Test fun `메모가 들어간다`() {
val d = tmp.newFolder("align")
writeCycles(d, 0, cycles = 1)