Commit graph

5 commits

Author SHA1 Message Date
유창욱
93f896af87 fix(audit): 1.2.0 4차 재감사 — 확정 23건 전량 해소
4개 관점 병렬 감사 후 각 발견을 반증 시도해 확정. 반증 5건은 문서에 남겼다.

이번 감사의 요지: **구현도 단위테스트도 초록인데 납품 경로에서 동작하지 않는**
결함이 셋 있었다. 전부 같은 모양이다 — 테스트가 기본값이 아닌 설정을 썼거나,
값이 존재해도 되는지 결정하는 상류 허용 목록을 보지 않았다.

CRITICAL 4건:

R4-1 추락 승격이 기본 설정에서 한 번도 발화하지 않았다. link_s(3.0)를 now
기준으로 쟀는데 넘어짐은 prone_confirm_s(3.0) 지속 후 확정되므로 확정 시점에는
항상 3초 넘게 지나 있다. 다른 테스트가 전부 prone_confirm_s=0.5 를 써서
초록이었다. 착지(prone 시작) 시각과 비교하도록 고치고, 기본값 그대로 도는
회귀 테스트를 넣었다.

R4-2 max_workers:0 이 상류 두 곳에서 막혀 있었다 — 서버 규칙 경계가 [1,50]
이라 저장이 400 이고, 엣지 파서가 v<=0 으로 규칙 전체를 무효화해 같이 넣은
2인1조까지 사라졌다. 앞 커밋에서 고친 네 곳은 전부 이 둘보다 하류라 도달
불가였다.

R4-3 고소작업 프리셋(elevated)이 완전히 무동작이었다. 서버 경계에 키가 없어
400, 통과해도 엣지 파서가 침묵 삭제. 프리셋 안내문과 커밋 메시지가 시스템이
하지 않는 일을 약속하고 있었다.

R4-4 동시 다발이 한 사람을 두 명으로 셀 수 있었다. 트래커는 1초 가려짐에
트랙을 버리고 새 id 를 주고(max_age=1.0) 누운 사람은 새 트랙에서도 즉시
prone 이라 두 번째 fall 이 난다. 그러면 다친 한 사람에게 "공기호흡기 없이
진입 금지"가 나가 구조가 늦어진다 — 기능의 가치가 정반대로 뒤집힌다.
ghost 를 인원 집계에서 빼고, 거의 같은 자리의 사건은 같은 사람으로 본다.

HIGH 5건: 구역 그룹핑 사문화(엣지가 zone_id 를 안 실어 카메라가 다른 두
희생자가 절대 안 묶였다 — 밀폐공간이 정확히 그 상황), 창 미끄러짐으로 한
사고가 매 분 새 위급 건으로 쏟아지고 인원이 줄면 앞선 알림을 스스로 뒤집던
문제, 무장 미만료·forget 누락·이중 발보, 업그레이드 안내의 마이그레이션 2개
누락(신규 유형 INSERT 가 전부 DB CHECK 에 걸린다), BUILD-INFO 커밋 스탬프.

BUILD-INFO 는 1.1.0 에서 고친 R3-6 의 재발이다. 절차로 막으려다 두 번
실패했으므로 HEAD 일치를 검사하는 테스트로 막았다. README 마이그레이션
목록도 같은 이유로 테스트를 붙였다.

MEDIUM 14건 중 눈에 띄는 것: 정비 억제가 SCBA 경고를 삼켰고(허가가 발급된
그 구역·그 시간이 질식이 일어나는 자리다) 위급 3종을 억제 예외로 뺐다.
데모 시더 심각도가 제품과 7종 어긋나 fall 이 위급으로 찍혀 1.2.0 의 요점인
넘어짐/추락 구분이 데모에서 지워져 있었다 — 라벨과 달리 화면이 안 깨져
눈으로는 못 잡으므로 엣지를 단일 출처로 강제하는 테스트를 붙였다.
문서의 "17종"은 실제 20종(엣지 16 + 서버 4)으로 정정하고 신규 2종을
소개서 감지 표에 넣었다.

검증: pytest 335 passed / phpunit 269 passed. 신규 테스트 30건.
dist 는 이 커밋 이후 재생성한다(BUILD-INFO 가 HEAD 를 가리켜야 한다).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBjuR7DW6PNNMows4KnbYU
2026-09-05 23:11:57 +09:00
유창욱
9c0cfd2fea feat(alert): 아무도 발행하지 않던 device_fault·monitoring_degraded 구현
두 유형은 타입 정의(edge/events.py)·서버 검증(EventValidator)·콘솔 필터·
통보 라벨까지 전 구간에 있었으나 생성하는 코드가 어디에도 없었다. 실제로
값을 넣는 것은 seed_demo.php 뿐이었고, 소개서가 내세운 "17종"은 15종이었다.

앞서 고친 EventValidator 누락과 정확히 반대 방향의 같은 병이다. 그때는
감지기가 있는데 서버가 막았고, 이번엔 서버·UI가 열려 있는데 감지기가 없다.
어느 쪽이든 화면은 멀쩡해 보인다.

엣지가 낼 수 없는 유형이라 서버에 둔다 — 전원·회선이 끊긴 노드는 자기가
죽었다고 신고할 수 없다. 하트비트의 부재로만 관측된다.

App\Alert\DeviceHealthWatch:
- dispatch_alerts.php 1분 크론의 1단계. 에스컬레이션·통보보다 앞에 두어
  같은 실행 안에서 발행된 장치 이벤트가 곧바로 통보 대상이 되게 했다.
- 멱등은 EscalationWatch와 같은 방식(event_uuid UNIQUE 충돌)이되, 장치
  건전성은 연속 상태라 uuid 앵커를 '장애 구간의 시작'으로 잡았다.
  device_fault는 last_heartbeat_at(장애 내내 불변) → 장애당 정확히 1건.
  monitoring_degraded는 창 안의 마지막 정상 하트비트 ts. 창 전체가 저하면
  시작을 볼 수 없으므로 창 버킷으로 폴백해 시간당 1회 재고지한다 —
  의도된 리마인더이고, 매 틱 재발행하는 앵커 드리프트가 아니다.
- 성능 저하는 지속을 요구(기본 5분). 한 번 튄 값에 울리면 운영자가 이
  유형을 무시하게 되고, 그러면 감시가 멈춘 날에도 아무도 보지 않는다.
  엣지 감지기들이 쓰는 규율과 같다.
- 하트비트를 한 번도 보낸 적 없는 장치는 장애가 아니다(미설치 노드).
  새 현장 첫날이 빨간 벽이 되면 그 사이트는 이 유형을 영영 신뢰하지 않는다.
- 죽은 노드에 degraded를 겹쳐 내지 않는다 — 한 고장에 두 알람은 소음이다.
- fps NULL을 0으로 강압하지 않는다(측정 실패 ≠ 정지). PdoDeviceMetric
  Repository가 NULL을 지키는 이유와 같다.
- 억제(정비 모드)는 여기서 보지 않는다. 기록은 남기고 통보 여부는
  AlertDispatcher가 정한다 — 정비 창은 전화를 끄는 것이지 노드가 죽었던
  기록을 지우는 것이 아니다.

camera_id는 NOT NULL인데 장치 단위 이벤트에는 카메라가 없다. '__device__'
센티넬을 쓰고, 콘솔은 "장치 전체"로 표시, 구역 편집기 카메라 목록에서는
제외한다(카메라 목록은 보고된 이벤트에서 파생되므로, 두면 존재하지 않는
카메라에 구역을 그리려는 일이 생긴다).

설정 4종 추가(device_stale_seconds 180 · degraded_sustain_seconds 300 ·
degraded_fps_ratio 0.6 · target_fps 12) — 두 config 템플릿에 한국어 설명과
함께. 낮은 프레임으로 운용하는 현장이 target_fps를 안 맞추면 전 노드가
영구 저하로 보고되므로 그 경고를 템플릿에 적었다.

검증: pytest 308 passed(3 skip: 모델 미동봉·shellcheck 미설치·AF_UNIX,
기존과 동일) · phpunit 240 passed(신규 16). dist 재생성으로 패리티 회복.
2026-08-25 10:36:48 +09:00
유창욱
9596d1bad6 release: 1.0.0 — 첫 정식 납품 버전
0.9.0 까지가 "감지하는 시스템"이었다면 1.0.0 은 "감지에 대응하는 시스템"이다. 그 차이를
만드는 것은 e00ad69 의 통보 경로이고, 이 커밋은 그것을 실제로 납품 가능한 형태로 만든다.

VERSION 0.9.0 -> 1.0.0

릴리스 규칙 위반 3건 수정 (rules/release-versioning.md)
- 산출물 파일명에 버전·날짜가 없었다. move-detect-cafe24.zip / 2_pi_install-<arch>.tar.gz
  는 같은 날 두 번 빌드하면 구분이 안 되고, 현장이 무엇을 돌리는지 파일명으로 답할 수
  없었다. 이제 move-detect-cafe24-<VERSION>-<YYYYMMDD>.zip /
  move-detect-pi-<VERSION>-<YYYYMMDD>-<arch>-<abi>-<hailo>.tar.gz.
- 두 빌드 스크립트가 VERSION 파일이 없으면 "unknown" 으로 넘어갔다. 무버전 산출물은
  빌드머신을 떠나는 순간 커밋과 대조할 방법이 사라진다 — 이제 exit 1 로 거부한다.
- deploy/README.md 의 stale 파일명 표기 갱신.

납품 동봉물 (skill delivery-audit: "설명서 + 점검 리포트 동봉")
- CHANGELOG.md 신설. 업그레이드 절차와 알려진 제약(모델 미확보 3종)을 명시.
- 역할별 사용 설명서를 패키지에 동봉. 운영자/안전관리자/시스템관리자 3역할 14절 + FAQ 6항,
  실제 화면 캡처 8장을 base64 로 박은 단일 HTML(665KB). 폐쇄망에서 메일·USB 로 돌아다녀도
  도판이 깨지지 않아야 하므로 단일 파일이다.
  생성물은 커밋하지 않는다 — 소스(manual.json + shots/)만 두고 패키저가 매번 빌드한다.
  커밋된 사본은 화면이 바뀐 뒤에도 최신처럼 보이고, 구 UI 캡처가 남은 매뉴얼은 납품물
  자체의 결함이다.
- 감사 리포트를 '납품점검-리포트.md' 로 동봉. 무엇을 점검했고 무엇이 잔여인지 패키지가
  스스로 답하지 못하면, 인수인계 몇 달 뒤 그 답을 아는 사람이 남지 않는다.

README-UPLOAD 에 크론 안내 신설 (누락이었다)
  설치 안내서에 자동 실행 등록이 아예 없었다. 이 릴리스의 핵심인 통보가 크론 없이는 한 건도
  나가지 않는데, 운영자가 읽는 유일한 문서가 그 사실을 말하지 않았다.
  8번(알림 켜기: 크론 2개 + --dry-run 선행 + 경광등 우선) / 9번(설치 점검 /setup) 추가,
  마지막 체크리스트에 3항목 추가.

부수 수정
- 셸 스크립트에 CRLF 가 섞여 bash 가 여는 중괄호에서 죽었다(빌드 자체가 불가). LF 로 정규화.
  cafe24 스크립트에는 bash -n 가드가 이미 있어 이번에 실제로 잡았고, Pi 쪽에는 없어서
  그대로 지나갈 뻔했다 — tests/test_deploy_artifacts.py 에 CRLF·bash -n 가드 추가.

산출물
  dist/move-detect-cafe24-1.0.0-20260818.zip
  dist/move-detect-pi-1.0.0-20260818-aarch64-cp311-hailo8.tar.gz

검증: pytest 통과(skip 3 = 하드웨어·환경) · PHPUnit 224 (skip 0).
매뉴얼 실렌더 확인(목차 정합·도판 8장·콜아웃). 패키지 내부 검증: 버전 스탬프 1.0.0,
마이그레이션 8/8 동봉, config.php 는 CHANGEME 템플릿, tools/ 는 www 밖에만,
웹 루트 PHP 는 index.php/_preflight.php 뿐.
2026-08-18 14:04:28 +09:00
유창욱
6b1f4c483f fix(security,delivery): scope improvement actions by site; correct upgrade procedure
프로젝트 8개 차원 감사(종합 82/100)에서 확정된 상위 3건 수정.

1. 개선 조치(move_actions)가 유일하게 site 스코프 규율에서 이탈 — 스코프 운영자가
   타 현장의 조치 기록(카메라 ID·메모·작성자)을 목록에서 읽고, 타 현장 카메라에
   조치를 기록해 그 현장 리포트의 전후 효과 지표를 오염시킬 수 있었다.
   - move_actions.site 컬럼 + (site, taken_at) 인덱스, 기존 행 백필 마이그레이션
   - 카메라 소유 현장은 이벤트에서 해석 (move_cameras는 등록 플로우가 없어 항상 비어 있음)
   - 오타 카메라는 400: 전후 효과가 영원히 0/0이 되는 조치를 애초에 못 만들게 한다
   - 타 현장 카메라는 404: 존재 여부를 숨기는 기존 규약 유지

2. README-UPLOAD 업그레이드 절차가 마이그레이션 7종 중 1종만 지시했다. move_ 접두
   리네임을 건너뛰면 이후 전부 실패하고 전 요청이 500이 된다 — 순서·적용조건·재실행
   비안전성을 표로 명시. 문서 4곳의 테이블 수(10→11)·비프리픽스 표기도 정정.

3. 두 납품 번들 재생성: 8/6 이후 4개 기능 커밋이 반영되지 않아 Report/Export/Actions
   컨트롤러와 8/7 마이그레이션이 빠진 채였다. 엣지 소스 패리티도 회복.

검증: pytest 235 passed / PHPUnit 147 passed (PDO 4 skip), 번들 산출물 내부 검증
(28 클래스 해석·전 파일 php -l·SHA256SUMS 55행·휠 11개·HEF 다이제스트 일치).
2026-08-07 18:06:32 +09:00
유창욱
3f2823a764 refactor: rename cloud/ to server/ (cafe24 is web hosting, not cloud)
The PHP app runs on Cafe24 shared web hosting -- calling it 'cloud'
was misleading. Renames the directory, the edge client module, and
every doc/script/identifier that used the term.

- cloud/ -> server/ (incl. server/deploy/build_cafe24_package.sh paths)
- edge/cloudclient.py -> edge/serverclient.py, CloudClient -> ServerClient
- cloud/docs/cloud-deploy.md -> server/docs/server-deploy.md
- CLOUD_* env/config keys -> SERVER_*
- tests renamed to match

Tests: PHP 109 pass (4 skipped), pytest 167 pass. dist/ regenerated.
2026-07-20 11:30:52 +09:00
Renamed from cloud/deploy/README-UPLOAD.md (Browse further)