UI/UX 점검에서 시작해 "이 제품이 안전 시스템으로서 무엇이 없나"까지 내려간 작업. 두 덩어리를 한 커밋으로 둔다 — 뒤쪽 기능들이 앞쪽에서 고친 화면 위에 얹혀 있다. ━━ 1부: UI/UX 감사 21건 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 동선 — 확인(ack) 경로가 위험했다 - 스트림 정렬을 '미확인 우선'에서 엄격 시간순으로 되돌린다. 확인 상태가 연대기를 뒤집어 사고 재구성이 불가능했고, 확인하면 그 행이 목록 밖으로 사라지며 아래 행이 밀려 올라와 같은 좌표를 두 번 누르면 다른 이벤트가 확인 처리됐다. 이제 제자리에서 '확인됨'으로 전환되고 확정 토스트가 뜬다. 처리 큐는 정렬이 아니라 뷰모드 필터로 분리 (전환해도 행끼리 상대 순서는 절대 안 바뀐다). - 실행취소는 넣지 않았다. acknowledged_by는 감사로그가 함께 남는 추가 전용 기록이라 무음 되돌리기가 안전 시스템에서 더 나쁜 속성이다. 대신 오탐·구역삭제를 2단계 확정으로. - 이벤트 상세에 확인/오탐 신설. 판단하는 화면에 판단 도구가 없었다. - 구역 설정에 장치·카메라 선택기. 시스템이 아는 값을 사람에게 묻고 있었고, 플레이스홀더(device-01)마저 실제와 다른 포맷을 가르쳤다. - 날짜 조회(키셋 커서 시딩). 사고 조사 진입로가 아예 없어 '더 보기'를 반복해야 했다. - 경보음 세션 음소거(새 위급 도착 시 자동 해제). 끌 수 없는 반복음은 안전 콘솔이 통째로 꺼지는 경로다. 전달 — 같은 단어가 화면마다 다른 숫자를 가리켰다 - KPI '미확인'(최근 200건)과 24시간 브리프 '미확인'이 40px 간격으로 충돌했다. KPI에 실제 집계 범위를 명시. 중대=위급+높음을 인라인 정의. - 페이지 명칭 3종·날짜 포맷 3종 통일. '확인 필요' 칩을 전 심각도로 확장 (그전엔 확인 여부가 먼 컬럼의 버튼 유무로만 구분됐다). - 표본 게이트: 오탐율 '100% (1/1)'이 '33% (5/15)'보다 위에 뜨던 것, 최다 3건짜리 시간패턴 히트맵, 6→8건에 붙던 빨간 ▲33%. 개선조치 효과는 푸아송 잡음 범위(√before)를 넘어야 방향을 주장한다 — 안전회의에서 한 번 반박당하면 카드 전체가 신뢰를 잃는다. - 카메라 위험 순위가 중대>0이면 전부 빨강이라 순위 노릇을 못 했다. 상대 기준으로. 시각 — 화면마다 완성도가 달랐다 - 구역 설정·이벤트 상세를 카드 셸로 재구성. 둘 다 스타일 없는 기본 폼이었고, 납품 심사에서 "덜 만든 티"는 정확히 거기서 났다. - 히트맵 토글은 aria-pressed만 있고 CSS 규칙이 없어 켜졌는지 눈으로 알 수 없었다. - 존 색을 팔레트와 정합(팔레트 어디에도 없던 보라 제거), 채움+라벨칩+협착 점선. 캔버스에 1px 외곽선만 그리면 실제 공장 프레임에서 사라진다. ━━ 2부: 안전 플랫폼 (마이그레이션 2026-08-09-safety-platform.sql) ━━━ 통보 — 이 제품의 정의에 해당하는 구멍 지금까지 감지는 브라우저 탭이 열려 있고 사람이 보고 있어야만 전달됐다. 새벽 3시에 넘어진 작업자는 DB에 행 하나를 남기고 아침을 맞았다. - 엣지 GPIO 경광등(edge/beacon.py). 인터넷·서버 없이 동작하는 유일한 경로라 어떤 현장이든 이것부터 설정한다. /api/config의 beacons로 배선을 받고, 하드웨어가 없거나 핀이 안 열리면 NullBeacon으로 떨어지되 그 사실은 반드시 로그로 남긴다. - 메일·웹훅 채널 + 크론 디스패처(tools/dispatch_alerts.php). 중복 발송은 읽고-쓰기가 아니라 (event,channel) UNIQUE로 막는다 — 크론 중첩 실행은 공유호스팅의 상수다. - 미확인 위급은 unacked_escalation 이벤트가 되어 상위 채널로 올라간다(event_uuid로 멱등). '아무도 보지 않았다'는 사실 자체를 사건으로 만든다. - 통보에 이미지는 싣지 않는다. 스냅샷은 PIPA 대상이고 메일링 리스트로 나가는 순간 인증 프록시가 지키던 통제를 벗어난다 — 링크만 보낸다. 조용한 실패 제거 - 카메라 화각 이동·가림·조도 저하 감지(detectors/integrity.py). 존 판정은 화각 고정을 전제로 서 있는데, 삼각대를 스치면 제한구역이 엉뚱한 바닥을 가리키면서 시스템은 계속 초록불이었다. numpy만 사용하고, 지나가는 사람과 구분하기 위해 지속시간을 요구한다. - 스토리지 여유 감시. 디스크가 차면 이미지 저장만 조용히 실패하고 보드는 멀쩡해 보인다. 끊겨 있던 배선 2건 - thresholds_from_config()가 정의만 있고 어디서도 호출되지 않았다. 서버에 표가 있고 /api/config가 성실히 실어 보내고 엣지가 파싱한 뒤 버렸다 — 관리자가 무엇을 바꿔도 장비는 부팅 시 edge.env 값으로 영원히 돌았다(edge/tuning.py). - 존·임계값 저장이 config_version을 올리지 않아 '설정 지연' 표시가 애초에 켜질 수 없었다. 지표는 있는데 그것을 켜는 사건이 없었다. 관리 화면 (그전에는 phpMyAdmin이 유일한 방법이었다) - 임계값 편집: 자유 텍스트가 아니라 카탈로그(범위·단위·설명)로 렌더한다. 범위 밖 값은 엣지가 기동 시 거부하는데 그때는 이미 공장 안 장비가 재시작 루프다. 키보드 앞에서 막는다. 목록에 없는 키는 저장 불가 — 장비가 조용히 무시할 값이 '설정됨'으로 보이면 안 된다. - 정비 억제: 사유 필수, 최대 8시간(한 교대), 자동 만료. 감지와 기록은 그대로 계속되고 통보만 보류되며 그 보류도 원장에 status='suppressed'로 남는다 — 조사에서 '경보가 났지만 의도적으로 withheld됐다'를 볼 수 있어야 한다. - 교대 인수인계, 위험성평가, 설비 성능 추이, 설치 점검(/setup — 실제 상태 조회 기반). 증거·규정 - 감사로그 해시체인. 공유호스팅은 append-only 권한을 줄 수 없으니 '막는' 대신 '숨길 수 없게' 만든다. 수정·삭제·우회삽입이 전부 탐지되고(tools/verify_audit_chain.php, 손상 시 exit 1), 체인 이전 행은 손상이 아니라 '검증 불가 구간'으로 따로 센다 — 정상 시스템이 매번 빨간불을 켜면 아무도 이 도구를 안 본다. - 사고조사 패키지 1클릭: 전후 타임라인 + 통보 이력(누구에게 닿았는가) + 위험요인 + 조치 이력 + 서명란. 인쇄 CSS로 그대로 제출 가능. 사고 다음 날 반나절을 쓸 사람은 없다. - 이벤트→조치 직접 기록, 현장별 롤업(전역 계정만). 운영 - 일괄 확인(위급은 의도적으로 제외·2단계 확정), 키보드 트리아지(J/K/A/F/Enter), 모바일 레이아웃(44px 타깃), 보존기간 정리(tools/maintain.php), 백업/복구(tools/backup.php — www 안 출력은 거부), 데모 시드(tools/seed_demo.php). - tools/는 전부 CLI 전용이고 납품 시 move_detect_app/tools/에 www 밖으로 배치된다. 미착수 4건 (모델·하드웨어가 선행 조건) - 보호구·중장비 근접·화재 감지는 AGPL 아닌 가중치 확보 + HEF 컴파일이 관문이다. 감지기 배선·이벤트 계약·심각도·쿨다운·테스트까지 만들고 기본 비활성으로 둔다 (edge/detectors/pending.py). enabled=True인데 model=None이면 아무것도 하지 않도록 명시적으로 막았다 — "곧 됩니다" 상태로 켜 두면 현장은 감지되고 있다고 믿는다. - PLC 접점 협착 게이팅도 같은 이유로 인터페이스만. 검증: pytest 통과(skip 3 = 하드웨어·환경) / PHPUnit 221 (skip 0, +59). Chrome 실렌더로 신규 4화면 + 임계값 저장→config_version 증가→감사 기록→체인 검증→ 행 변조→탐지(exit 1) 전 경로 확인. cafe24 번들 + Pi 오프라인 번들(aarch64 휠 11개) 재생성, 소스 패리티 통과. 보안 자체점검: SQL 문자열 보간 0, innerHTML 0, 상태 변경 핸들러 전부 CSRF+RBAC.
11 KiB
INDEX — 작업 기록 (완료 결과만)
-
2026-08-09 안전 플랫폼 확장 — 로드맵 23건 중 소프트웨어로 완결 가능한 19건 반영. 마이그레이션
2026-08-09-safety-platform.sql(표 6종 신설 + 컬럼 4종 + event_type CHECK 확장). 통보(제품 정의 수준의 구멍): 지금까지 감지는 브라우저를 보고 있는 사람에게만 전달됐다. ① 엣지 GPIO 경광등(edge/beacon.py) — 네트워크 없이 동작하는 유일한 경로, /api/config 로 설정 수신 ② 메일·웹훅 채널 + 크론 디스패처(tools/dispatch_alerts.php,--dry-run지원) ③ 미확인 위급 →unacked_escalation이벤트 발행(멱등) → 상위 채널로 에스컬레이션. 중복 발송은 (event,channel) UNIQUE 로 막는다(크론 중첩 실행 대비). 조용한 실패 제거: 카메라 화각 이동·가림·조도 저하 감지(detectors/integrity.py) — 존 판정이 무의미해진 것을 처음으로 알 수 있게 됨. 스토리지 여유 감시. 엣지에 닿지 않던 배선 2건 수리:thresholds_from_config()가 파싱만 하고 버려서 임계값이 장비에 적용되지 않던 것(edge/tuning.py), 존/임계값 저장 시config_version을 올리지 않아 설정 드리프트 표시가 영원히 꺼져 있던 것. 관리 화면 신설: 임계값 편집(카탈로그 기반 범위 강제 — 엣지가 기동 시 거부하는 값을 키보드 앞에서 막는다) · 통보 채널 · 정비 억제(사유 필수·최대 8시간·감지는 계속되고 통보만 보류) · 위험성평가 · 교대 인수인계 · 설비 성능 추이 · 설치 점검(/setup, 실제 상태 조회 기반). 증거·규정: 감사로그 해시체인(수정·삭제·우회삽입 전부 탐지,tools/verify_audit_chain.php) · 사고조사 패키지 1클릭(전후 타임라인+통보 이력+위험요인+조치 이력+서명란, 인쇄 CSS) · 이벤트→조치 직접 기록 · 현장별 롤업(전역 계정만). 운영: 일괄 확인(위급 제외·2단계) · 키보드 트리아지(J/K/A/F/Enter) · 모바일 레이아웃 (44px 타깃) · 보존기간 정리(tools/maintain.php) · 백업/복구(tools/backup.php, www 안 출력 거부) · 데모 시드(tools/seed_demo.php). 검증: pytest 통과(3 skip = 하드웨어·환경) · PHPUnit 221 (skip 0, +59) · Chrome 실렌더로 신규 4화면 + 임계값 저장→config_version 증가→감사 기록→체인 검증→변조 탐지 전 경로 확인 · cafe24 번들 + Pi 오프라인 번들(aarch64 휠 11개) 재생성, 소스 패리티 통과. 미착수 4건(모델·하드웨어 선행): 보호구·중장비 근접·화재 감지는 AGPL 아닌 가중치 확보와 HEF 컴파일이 관문이라 감지기 배선·이벤트 계약·테스트만 완성하고 기본 비활성으로 둠 (edge/detectors/pending.py). PLC 접점 협착 게이팅도 같은 이유로 인터페이스만. -
2026-08-09 UI/UX 전면 점검 후 21건 일괄 반영 (동선 7 · 전달 9 · 시각 5). 동선: ① 스트림 정렬을 '미확인 우선'에서 엄격 시간순으로 되돌림 — 확인 상태가 연대기를 뒤집어 사고 재구성이 불가능했고, 확인 시 행이 목록 밖으로 사라져 아래 행이 밀려 올라오며 오클릭으로 다른 이벤트가 확인 처리될 수 있었다. 확인은 제자리 전환 + 확정 토스트로 변경. ② 처리 큐는 정렬이 아니라 뷰모드(전체/미확인만) 필터로 분리. ③ 이벤트 상세에 확인/오탐 액션 신설(판정 화면에 판정 도구가 없던 문제). ④ 오탐·구역삭제 2단계 확정(되돌릴 수 없는 동작). ⑤ 구역 설정에 장치·카메라 선택기(자유 텍스트 ID 암기 제거, 가짜 플레이스홀더 제거). ⑥ 날짜 조회(키셋 커서 시딩, 최대 6페이지) — 사고 조사 진입로. ⑦ 경보음 세션 음소거 (새 위급 도착 시 자동 해제). 전달: KPI 스트립에 실제 집계 범위 표기(24h 브리프와 숫자 충돌 해소), 중대=위급+높음 인라인 정의, 페이지 명칭 3종·날짜 포맷 3종 통일, '확인 필요' 칩을 전 심각도로 확장, 상세
CONFIDENCE→검출 신뢰도 %, 오탐율·시간패턴 표본 게이트, 개선조치 효과에 푸아송 잡음 범위(√before) 판정 보류, 카메라 위험 순위 강조 기준을 상대값으로, 날짜 구분선. 시각: 구역 설정·이벤트 상세를 카드 셸로 재구성(둘 다 스타일 없는 기본 폼이었음), 히트맵 토글 aria-pressed 시각 상태 신설(CSS 규칙 자체가 없었음), 존 색을 팔레트 정합 (팔레트 외 보라 제거, 채움+라벨칩+협착 점선), 주 동작 accent 승격, 장치명 플리커 제거. 검증: pytest 통과 · PHPUnit 162 (skip 0) · Chrome 실렌더로 4개 화면 + 확인/오탐/날짜조회/ 삭제 2단계/히트맵 토글 실동작 확인 · cafe24 번들 재생성(소스 패리티 통과). 미해결(설계 판단): 확인 취소(undo)는 넣지 않음 —acknowledged_by는 감사 로그가 남는 추가 전용 기록이라, 무음 되돌리기는 안전 시스템에서 더 나쁜 속성. 서버에 unack 라우트를 두는 안은 별도 결정 필요. -
2026-08-08 감사 잔여 확정 항목 일괄 처리 (VERSION 0.9.0 신설). 보안: CSP nonce 도입(인라인 2블록, unsafe-inline 없음)+보안헤더를 PHP로 이관(개발/납품 동일), /logout CSRF, 계정 잠금 423→401(사용자명 열거 제거). 엣지: 큐 flush 배치 상한(정전 복구 시 감지 정지 해소), 타이머 env 전수 NaN/음수 검증, T2 프레임 스킵(
INFER_EVERY, 스킵 틱은 링버퍼 미투입 =미블러 얼굴 유출 차단), systemd 워치독(sd_notify)+메모리 상한, UVC 해상도·버퍼 고정. 테스트: PDO 통합 테스트 스킵 해소(-d extension=pdo_sqlite, 리포트·히트맵·익스포트 집계 SQL 최초 실행), 얼굴 블러 테스트를 실제 증거로 교체(무동작 퇴화 감지), index.php 라우트 테이블 대조(프런트 호출 15개 교차검증), dist↔소스 패리티 테스트, CI 신설 (.forgejo/workflows/verify.yml, 스킵 발생 시 실패). 프런트: 이벤트 목록 시그니처 기반 재렌더 스킵+포커스 복원(5초마다 포커스 소실·전체 재낭독 해소), 폴링 중첩 가드, 콜드스타트 실패 표시. 문서: deploy/README·env.example을 Hailo 단독 번들 실제와 일치, bringup의 미존재 설정 키를 실제 env 노브로 교체. 납품물에 version/git_commit/built_at 스탬프. pytest 251 / PHPUnit 162 (스킵 0). -
2026-08-08 프로젝트 8개 차원 감사(종합 82/100) 후 상위 3건 수정. ①개선 조치 site 스코프 누락(스코프 운영자가 타 현장 조치 기록 열람·기록 가능) →
move_actions.site추가 (2026-08-08-actions-site.sql, 이벤트에서 카메라 소유 현장 해석, 오타 카메라 400·타 현장 404). ②README-UPLOAD 업그레이드 절차가 마이그레이션 1/7만 지시 → 순서·적용조건 표로 교체 (move_접두 리네임 누락 시 전 요청 500). ③두 납품 번들 재생성(4커밋 스테일 해소 — Report/Export/Actions 컨트롤러·마이그레이션 누락 복구, 엣지 소스 패리티 회복). 문서 테이블 수·프리픽스 표기 정정. pytest 235 / PHPUnit 147. -
2026-08-07 데이터 활용 5안 중 4안 구현: ②시간패턴 히트맵+전조 급증 경보 ③KPI 전기간 추이(
d8b8e1f) ①라벨 JSONL 익스포트(/api/export/labels, 관리자) ④개선 조치 기록·전후 효과 비교(move_actions, 동일 창 비교, 3일 미만 "측정 중"). ⑤멀티사이트 벤치마크는 다현장 이후(사이트 스코프 구조가 이미 수용). PHPUnit 145 + Chrome 실렌더. -
2026-08-07 증거 타임라인 + 24시간 캐치업: 이벤트 스냅샷이 링버퍼 기반 과거→발생 3장(프레임별 자기 시점 얼굴 블러, P1-B 잔여 종결), 상세뷰 이전/직전/발생 캡션. 콘솔 첫 진입 '지난 24시간' 요약 배너(/api/report?days=1 재사용, 리포트 링크). pytest 235/ PHPUnit 138 + Chrome 실렌더.
-
2026-08-07 안전 운영 리포트
/report(주간 안전회의용 인쇄 가능 한 장: 중대 이벤트· TTA(FP 제외)·니어미스 피라미드·오탐율·유형/카메라 순위·인체공학 노출·존 규칙 위반) + 이벤트 스트림 유형 필터. PHPUnit 138 + Chrome 실렌더(KPI/바리스트/타일/필터). -
2026-08-06 인체공학 자세부담 스코어링 (
ergo_report/INFO, 카메라 단위 익명·스냅샷 미부착): REBA 밴드 노출시간(허리 20~60°/60°+·팔 90°+·웅크림<110°), 사고 상태 트랙 제외, 검출 공백 dt 상한. 상세뷰 지표 한글 렌더 —b358822. pytest 232/PHPUnit 135 + 실렌더. -
2026-08-06 납품 번들 재생성:
dist/1_cafe24_upload+move-detect-cafe24.zip(마이그레이션 5종 동봉 — 빌드 스크립트에 staging 추가) ·dist/2_pi_install--cross 재빌드 (aarch64/cp311 휠 11종 + HEF 다이제스트 검증 + SHA256SUMS). FLAG: MoveNet ONNX 업스트림 URL 404 → 이번 Pi 번들은 Hailo 단독(CPU 폴백 모델 미포함).edge/tools/fetch_movenet.py의 소스 URL 갱신 필요. -
2026-08-06 오탐 피드백 (결정 a: 통계만, 자동 튜닝 없음): 이벤트 오탐 태그 (
POST /events/{id}/false-positive, ack 겸함, 최초 태거 보존) +GET /api/fp-stats+ 콘솔 오탐율 패널(30일, 30%+ 강조). PHPUnit 135 + Chrome 실렌더(태그→행 전환→패널 즉시 갱신). -
2026-08-06 대시보드 장치→카메라 트리 레일 + (device,camera) 쌍 카메라 필터 —
eaff176. PHPUnit 119 통과 + Chrome 실렌더 검증(트리 상태·필터·접기·콘솔 무오류). -
2026-08-06 감사 잔여 P2-6/7/8 종결 (ID 스왑 게이트 · sustained-prone FALLING · stale 트랙 no-data) —
32f2237. TDD(RED 확인), pytest 203 통과/2 skip. → delivery-audit 오프하드웨어 항목 43/43 완료. 잔여는 실장비(OFF-HW) 검증뿐. -
2026-08-06 연속성 문서 신설:
CLAUDE.md·docs/PROJECT_MAP.md· 본 INDEX. -
2026-08-06 니어미스 히트맵:
near_fall(FALLING→확정 전 회복, INFO) + 전 트랙 이벤트에 정규화 발좌표(spatial) 부착 →move_events.spatial_x/y적재 →GET /api/heatmap/{camera}→ 존 편집기 '위험 히트맵' 오버레이. 직전 커밋의 chk_events_type 제약 누락(신규 타입 INSERT 거부) 마이그레이션으로 수정. pytest 222/PHPUnit 130 + Chrome 실렌더. -
2026-08-06 존 점유 규칙 엔진 (2인1조
lone_worker/HIGH · 정원overcapacity· 체류상한overstay): edgeoccupancy.py+move_zones.rules_json+ 존 편집기 규칙 입력 + 이벤트 유형 한글 라벨. pytest 216/PHPUnit 123 통과 + Chrome 실렌더(편집기 저장·목록·콘솔 라벨).