move_detect/server/src/View/event_detail.php
유창욱 3aca674a78
Some checks failed
verify / edge (push) Has been cancelled
verify / server (push) Has been cancelled
feat: 이해관계자 페르소나 검증 4건 — 열람 감사 · 증거 보존 · 크론 생존 · 증거 정책 (1.3.0)
앞선 감사 네 번은 "이 코드가 제대로 도는가"를 물었다. 이번엔 작업자·노조·안전관리자·
공장장·설치기사 5인 관점으로 3단논법을 세우고 전제를 코드로 확인한 뒤 반문으로 깼다.
5인 중 2인의 논증이 완전히, 2인이 부분적으로 무너졌다.

읽기 감사 — 이 시스템은 쓰기를 전부 감사했다. 임계값 한 번 바꾼 것까지 해시 체인에
들어간다. 그런데 작업자 사진을 열어보는 것은 하나도 남지 않았다. 감시 시스템과 안전
시스템을 가르는 선이 정확히 거기라서, 나머지 프라이버시 설계(RAM 링버퍼 7.5초·전송 전
얼굴 블러·라이브뷰 부재)가 전부 이 한 줄이 없어서 검증 불가능한 주장에 머물러 있었다.
거부된 열람은 기록하지 않는다 — 그러면 감사 로그가 "본 사람"이 아니라 "요청한 사람"
목록이 되어 정작 누가 봤는지를 묻는 데 못 쓴다.

크론 생존 — 죽은 노드는 스스로 신고할 수 없어서 크론이 하트비트의 부재로 판정한다.
그런데 그 크론이 멈추면 아무도 판정하지 않는다. 통보·5분 확대·장치 이상 발행이 전부 그
한 프로세스에 걸려 있어서, 멈춘 순간부터 화면은 조용해지고 그 침묵은 "사고 없음"과
구별되지 않는다. 감시자의 생존은 감시자가 아닌 쪽이 읽어야 한다.

증거 보존 — 스냅샷 삭제가 나이 하나만 봤다. 기본 7일인데 중대재해 조사는 몇 주에서 몇
달이라, 우리가 내건 목적("사고 후 원인 분석")이 8일째에 스스로 사라졌다. 확인 처리로
자동 연장하지 않는 이유는 자동이면 사실상 전량 보존이 되어 7일 약속이 무너지기 때문이다.

증거 정책 — 프레임이 ergo_report 하나만 빼고 모든 이벤트에 붙었다. 체류 상한 초과도
포함이라, 밀폐공간 프리셋 기본값(30분) 기준으로 정상 작업자가 규정대로 일하는 동안
주기적으로 본인 사진이 서버로 갔다. 위험해서가 아니라 오래 있어서. 기준을 명문화했다:
시간 경과 자체가 조건인 이벤트에는 사진을 붙이지 않는다.

마이그레이션 2026-09-25-evidence-hold-and-view-audit.sql (README-UPLOAD 10번).

검증: phpunit 302 passed(신규 18, 스킵 0) · pytest(신규 18)

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

55 lines
2.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require __DIR__ . '/layout.php';
render_header('이벤트 상세');
?>
<div class="detail">
<nav class="detail-crumb"><a href="<?= view_url('/') ?>">감지 스트림</a><span aria-hidden="true">›</span><span>이벤트 상세</span></nav>
<header class="detail-head">
<div class="detail-headline">
<h1 id="detail-title">이벤트 상세</h1>
<p id="detail-when" class="detail-when">불러오는 중…</p>
</div>
<!-- 판정은 증거를 본 화면에서 끝난다. 예전에는 여기서 판단하고 목록으로 되돌아가
그 행을 다시 찾아 눌러야 했다 — 판단하는 자리에 판단 도구가 없었다. -->
<div class="detail-actions" id="detail-actions" hidden></div>
<div class="detail-actions" id="detail-hold" hidden></div>
</header>
<div class="detail-links">
<a id="detail-package" class="detail-link-btn" href="#">사고조사 패키지 →</a>
<a id="detail-zones" class="detail-link-btn" href="#">이 카메라 구역 설정 →</a>
</div>
<div class="detail-grid">
<section class="detail-card detail-evidence">
<h2 class="detail-card-title">증거 프레임</h2>
<div id="event-detail-snapshots"></div>
<p class="detail-foot">프레임은 발생 시점을 향해 왼쪽에서 오른쪽으로 이어집니다. 프레임 간격은 엣지의 스냅샷 주기 설정을 따릅니다.</p>
</section>
<aside class="detail-side">
<div class="detail-card detail-meta">
<h2 class="detail-card-title">이벤트 정보</h2>
<dl id="event-detail-meta" role="status" aria-live="polite" aria-busy="true">
<div class="detail-row"><dt>상태</dt><dd>불러오는 중…</dd></div>
</dl>
</div>
<!-- 사건에서 조치로 바로 이어지게 한다. 조치를 리포트 화면에서만 기록할 수 있으면
"무엇이 계기였나"가 끊기고, 나중에 그 대책이 어느 사고에서 나왔는지 알 수 없다. -->
<div class="detail-card detail-action-card" id="detail-action-card">
<h2 class="detail-card-title">개선 조치 기록</h2>
<p class="detail-card-hint">이 사건을 계기로 취한 조치를 남기면 전후 감지 밀도를 자동 비교합니다.</p>
<form id="detail-action-form">
<label class="field"><span class="field-label">조치 내용</span>
<input type="text" id="detail-action-note" maxlength="200" placeholder="예: 진입로 미끄럼 방지 매트 설치">
</label>
<button type="submit" class="btn-primary">조치 기록</button>
</form>
<p id="detail-action-status" role="status" class="admin-status"></p>
</div>
</aside>
</div>
</div>
<script src="<?= view_url('/assets/device-scope.js') ?>"></script>
<script src="<?= view_url('/assets/detail.js') ?>"></script>
<?php
render_footer();