move_detect/docs/fall-sequence.archify.json
유창욱 34506e150d docs: 설계 도해 3종 (구성 · 낙상 시퀀스 · 낙상 상태기계)
archify 소스 JSON 만 커밋한다. HTML 은 생성물이라 사용설명서와 같은 이유로
gitignore — 소스가 바뀐 뒤에도 낡은 사본이 최신처럼 보이기 때문.

시퀀스를 그리면서 드러난 것: 경광등은 확정 즉시(3초)지만 메일·웹훅은
크론 주기를 기다려 최대 63초다. 문서가 둘을 "통보"로 묶어 쓰던 표현이
실제로는 20배 차이나는 두 경로였다.

상태기계는 확정 창 3초와 추락 연결 창 3초가 겹쳐 있는 구조를 한 장에
담았다 — 4차 감사에서 잡은 CRITICAL(추락 승격 미발화)의 원인이고,
시간축을 그렸다면 코드를 읽기 전에 보였을 충돌이다.

검증: deliver 9/9 · 오류 0 · 경고 0, visual-check 1440x900 / 1600x1000 /
1920x1080 / 2048x1320 양 테마 containment·가독성 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBjuR7DW6PNNMows4KnbYU
2026-09-18 22:59:17 +09:00

258 lines
5.5 KiB
JSON

{
"schema_version": 1,
"diagram_type": "sequence",
"meta": {
"title": "낙상 1건 — 감지에서 통보까지",
"output": "move-detect-fall-sequence.html",
"quality_profile": "showcase",
"column_fit": "spread",
"views": [
{
"id": "on-site",
"label": "현장에서 끝나는 3초",
"focus": [
"cam",
"edge",
"beacon"
],
"note": "확정부터 점등까지 서버를 거치지 않는다. 회선이 끊겨도 여기까지는 동작한다."
},
{
"id": "what-leaves",
"label": "밖으로 나가는 것",
"focus": [
"edge",
"api",
"db"
],
"note": "영상이 아니라 얼굴 블러된 3프레임. 블러는 전송 전, 노드에서."
},
{
"id": "notify-lag",
"label": "통보까지의 실제 지연",
"focus": [
"cron",
"db",
"staff"
],
"note": "경광등은 즉시, 메일은 크론 주기만큼 늦다. 그 간격이 이 그림의 요점이다."
}
],
"viewBox": [
1060,
560
]
},
"participants": [
{
"id": "cam",
"type": "external",
"label": "카메라",
"sublabel": "12fps"
},
{
"id": "edge",
"type": "backend",
"label": "엣지 노드",
"sublabel": "Pi 5 + Hailo-8"
},
{
"id": "beacon",
"type": "external",
"label": "경광등",
"sublabel": "GPIO"
},
{
"id": "api",
"type": "security",
"label": "수집 API",
"sublabel": "HMAC 검증"
},
{
"id": "db",
"type": "database",
"label": "MySQL",
"sublabel": "move_events"
},
{
"id": "cron",
"type": "backend",
"label": "1분 크론",
"sublabel": "dispatch_alerts"
},
{
"id": "staff",
"type": "external",
"label": "담당자",
"sublabel": "메일 · 웹훅"
}
],
"segments": [
{
"from": 152,
"to": 190,
"label": "감지 — 자세 3초"
},
{
"from": 198,
"to": 320,
"label": "현장 대응 · 기록"
},
{
"from": 330,
"to": 366,
"label": "미동 없음 13초"
},
{
"from": 374,
"to": 466,
"label": "통보 — 크론 주기 지연"
}
],
"messages": [
{
"id": "frames",
"from": "cam",
"to": "edge",
"y": 168,
"label": "프레임 · 12fps",
"variant": "default",
"note": "0.0초 쓰러짐 → 1초 위험 자세 → 3초 확정"
},
{
"id": "gpio",
"from": "edge",
"to": "beacon",
"y": 206,
"label": "넘어짐 확정(3초) → GPIO 점등",
"variant": "emphasis",
"note": "3.0초 · 서버도 인터넷도 거치지 않는다"
},
{
"id": "post",
"from": "edge",
"to": "api",
"y": 244,
"label": "HMAC POST · 3프레임",
"variant": "security",
"note": "얼굴 블러는 전송 전, 노드에서"
},
{
"id": "insert",
"from": "api",
"to": "db",
"y": 276,
"label": "INSERT",
"variant": "default"
},
{
"id": "accepted",
"from": "api",
"to": "edge",
"y": 306,
"label": "202 Accepted",
"variant": "return"
},
{
"id": "nomotion",
"from": "edge",
"to": "api",
"y": 344,
"label": "미동 없음 · 10초",
"variant": "emphasis",
"note": "13.0초 — 최상위 위급"
},
{
"id": "poll",
"from": "cron",
"to": "db",
"y": 384,
"label": "통보 대상 조회 · 최대 60초 뒤",
"variant": "default",
"note": "최대 60초 뒤 — 크론이 1분마다 돈다"
},
{
"id": "send",
"from": "cron",
"to": "staff",
"y": 418,
"label": "메일 · 웹훅 (링크만)",
"variant": "emphasis"
},
{
"id": "escalate",
"from": "cron",
"to": "db",
"y": 452,
"label": "5분 미확인 → 상위 확대",
"variant": "dashed"
}
],
"activations": [
{
"participant": "edge",
"from": 196,
"to": 352,
"type": "backend"
},
{
"participant": "beacon",
"from": 201,
"to": 232,
"type": "external"
},
{
"participant": "api",
"from": 239,
"to": 312,
"type": "security"
},
{
"participant": "db",
"from": 271,
"to": 300,
"type": "database"
},
{
"participant": "cron",
"from": 379,
"to": 458,
"type": "backend"
},
{
"participant": "staff",
"from": 413,
"to": 442,
"type": "external"
}
],
"cards": [
{
"dot": "cyan",
"title": "현장에서 끝나는 3초",
"items": [
"자세가 3초 지속돼야 확정한다",
"확정 즉시 GPIO 점등 — 서버 불필요",
"서버가 죽어도 여기까지는 동작"
]
},
{
"dot": "rose",
"title": "밖으로 나가는 것",
"items": [
"영상이 아니라 3프레임",
"얼굴 블러는 전송 전, 노드에서",
"블러 전 이미지는 서버에 없다"
]
},
{
"dot": "amber",
"title": "두 지연은 다르다",
"items": [
"경광등은 즉시",
"메일은 크론 주기만큼 늦다",
"\"1분 통보\"는 대기를 포함한 값"
]
}
]
}