---
template: policy-doc@1
doc_id: 03-monitoring
title: 상태 감시 — 분석 · 알림톡 · 팩스의 임계와 전이
config: ../_meta/template-config.yaml
last_verified: 4d798f55
policies:
  - id: monitoring:alimtalk-status
    status: conditional-unknown
    evidence: [A-scheduler, A-comment]
    unknown_reason: 이 규칙의 발효는 담당 배치의 활성에 걸려 있고 그 활성은 판정되지 않았다 — 04-runtime-topology.md#schedule:alimtalk-status 가 그 미판정의 근거와 남은 입력을 진다. 이 규칙에는 기간 임계가 없다 — 대상은 발송요청 성공 후 완료 상태가 비어 있는가로 정해진다.
    anchors:
      - insuniverse-server/apps/api/src/schedules/alimtalk-status.schedule.ts:22
      - insuniverse-server/apps/api/src/schedules/alimtalk-status.schedule.ts:27
      - insuniverse-server/apps/api/src/modules/alimtalk/alimtalk.service.ts:648
  - id: monitoring:analysis-stall
    status: conditional-unknown
    evidence: [A-scheduler, A-comment]
    unknown_reason: 이 규칙의 발효는 담당 배치의 활성에 걸려 있고 그 활성은 판정되지 않았다 — 04-runtime-topology.md#schedule:analysis-stall 이 그 미판정의 근거와 남은 입력을 진다. 임계 자체는 동결 stg 4d798f55 의 코드 행으로 확정되며 주석과 갈리지 않는다.
    anchors:
      - insuniverse-server/apps/api/src/schedules/analysis-stall.schedule.ts:24
      - insuniverse-server/apps/api/src/schedules/analysis-stall.schedule.ts:28
      - insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:288
      - insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:292
  - id: monitoring:analysis-status
    status: conditional-unknown
    evidence: [A-scheduler, A-comment]
    unknown_reason: 이 규칙의 발효는 담당 배치의 활성에 걸려 있고 그 활성은 판정되지 않았다 — 04-runtime-topology.md#schedule:analysis-status 가 그 미판정의 근거와 남은 입력을 진다. 이 배치가 최상위에서 부르는 세 동작 중 셋째는 관리자 설정에도 걸려 있어 활성이 확인되더라도 발송 여부가 데이터로 갈린다.
    limitations:
      - 이 배치는 최상위에서 세 동작을 차례로 부른다 — 인증 stale 정산 · 예약수 정합 보정 · 모니터링 경고 발송. 정책 id 는 배치 단위 하나이고 세 동작의 좌표는 anchors 가 전건 담는다
      - 셋째 동작(모니터링 경고 발송)은 어드민이 켠 경고 종류에만 반응한다. 그 설정은 운영 DB 에 있어 저장소로 판정할 수 없다 — 활성이 확인되더라도 실제 발송 여부는 그 데이터가 정한다
      - 인증 stale 임계에서 코드와 주석이 갈린다. 근거 대조표의 판정 불일치 행이 그 자리다
    anchors:
      - insuniverse-server/apps/api/src/schedules/analysis-status.schedule.ts:25
      - insuniverse-server/apps/api/src/schedules/analysis-status.schedule.ts:29
      - insuniverse-server/apps/api/src/schedules/analysis-status.schedule.ts:33
      - insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:259
      - insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:265
      - insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.service.ts:410
      - insuniverse-server/apps/api/src/modules/treat/treat-alert.service.ts:6
  - id: monitoring:fax-status
    status: conditional-unknown
    evidence: [A-scheduler, A-comment]
    unknown_reason: 이 규칙의 발효는 담당 배치의 활성에 걸려 있고 그 활성은 판정되지 않았다 — 04-runtime-topology.md#schedule:fax-status 가 그 미판정의 근거와 남은 입력을 진다. 여기에 더해 응답 대기 임계에서 스케줄러 주석과 서비스 코드가 갈리며 이 문서는 어느 쪽이 옳은지 판정하지 않는다.
    limitations:
      - 응답 대기 임계에서 코드와 주석이 갈린다. 스케줄러 docblock 은 3시간이라 적고 서비스 상수는 1시간이다. 근거 대조표의 판정 불일치 행이 그 자리이며 어느 쪽이 옳은지는 저장소 소유자 소관이다
    anchors:
      - insuniverse-server/apps/api/src/schedules/fax-status.schedule.ts:28
      - insuniverse-server/apps/api/src/modules/fax/fax.service.ts:1173
      - insuniverse-server/apps/api/src/modules/fax/fax.service.ts:1195
---

# 상태 감시 — 분석 · 알림톡 · 팩스의 임계와 전이

*"stall 감시가 잡은 건은 어떤 상태로 바뀌는가"* 의 답을 저장소 안에서 찾은 결과다. 감시 규칙은
넷이고, 각 규칙은 **얼마를 기다린 뒤 어떤 상태로 옮기는가** 로 이뤄진다. 그 기다림의 값은
동결 stg `4d798f55` 트리의 코드 행이 정하고 `## 근거 대조표` 가 그 원문을 싣는다.

**이 문서에는 코드와 주석이 갈리는 자리가 둘 있다.** 인증 stale 임계와 팩스 응답 대기 임계다.
문서는 어느 쪽이 옳은지 판정하지 않고 **둘 다 앵커와 함께 싣고 갈림을 표기**한다 — 코드 정정은
저장소 소유자 소관이고 이 창은 문서만 만든다. 판정을 미루는 것이 아니라, 소스가 두 값을 말하고
있다는 사실 자체가 관측이다.

`04-runtime-topology.md` 가 확정한 것은 이 배치들의 *선언* 이었고 **어떤 임계로 무엇을 바꾸는지는
그 문서가 적지 않았다.** 반대로 배치가 지금 실제로 도는지는 이 문서도 답하지 않으며, 그 판정에
필요한 입력은 각 정책의 `unknown_reason` 이 M1 항목을 이름으로 가리켜 넘긴다.

상태 전이가 부르는 도메인 로직(환불 계산 · 알림 발송 규칙 · 예약수 정합 산식)은 `50-domain`
소유다. 이 문서는 진입 조건과 임계까지만 적는다.

## 정책 (기획)

**이 절에는 정책 블록이 없다. 감시 규칙 넷 전부가 `## 부록` 에 있다.**

감시 규칙은 *임계가 얼마인가* 만으로 서지 않는다. 임계를 넘긴 건이 실제로 실패 상태로 바뀌려면
담당 배치가 그 주기로 호출돼야 하고, 그 호출은 EventBridge 규칙과 ECS task-def 가 정하며 **둘 다
저장소 밖에 있다.** 임계만 본문에 올리면 독자는 *지금 걸려 있는 감시 규칙* 으로 읽는다.

감시 축에는 겹이 하나 더 있다. 분석 상태변경 배치의 셋째 동작은 **관리자가 켠 경고 종류**에만
반응하고 그 설정은 운영 DB 에 있다. 활성이 확인되더라도 발송 여부는 그 데이터가 정한다 — 즉 이
축은 저장소만으로는 두 겹이 함께 미확인이다.

## 근거 대조표

각 행은 규칙 값 하나에 대한 **코드 측 주장과 주석 측 주장**을 나란히 싣는다. 두 원문은 동결
stg `4d798f55` 트리의 그 행 그대로이고, `기준값` 은 코드 측 정규 쌍의 표준 표기(코드 측이 없으면
주석 측)이며, `판정` 은 게이트가 두 원문에서 다시 유도해 계산한다. **행 집합은 이 문서가 고르지
않는다** — 담당 스케줄러의 docblock 수량행과 임계 심볼 스팬에서 유도된 것 전부이며, 유리한 짝만
싣는 것이 구조적으로 불가능하다.

| 정책 id | 기준값 | 코드 앵커 | 코드 원문 | 주석 앵커 | 주석 원문 | 판정 |
|---|---|---|---|---|---|---|
| `monitoring:alimtalk-status` | 5분 | — | — | `insuniverse-server/apps/api/src/schedules/alimtalk-status.schedule.ts:8` | ` * 동작주기 : 5분 (참고 : 유저측에 알림톡목록이 있을 경우 조금 더 느슨하게 가도 됨)` | 코드 없음 |
| `monitoring:alimtalk-status` | — | `insuniverse-server/apps/api/src/modules/alimtalk/alimtalk.service.ts:648` | `  async getAlimtalkForSchedule() {` | — | — | 임계 없음 |
| `monitoring:analysis-stall` | 1시간 | `insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:292` | `    const subDate = dayjs().subtract(1, 'hour').format('YYYY-MM-DD HH:mm:ss');` | `insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:288` | `  /** 자료수집완료(ODD002) / 자료분석중(ODD005) 인데 1시간 경과 + 최종분석상태 미박힘` | 일치 |
| `monitoring:analysis-stall` | 10분 | — | — | `insuniverse-server/apps/api/src/schedules/analysis-stall.schedule.ts:9` | ` * 동작주기 : 10분 (AWS EventBridge)` | 코드 없음 |
| `monitoring:analysis-stall` | 1시간 | — | — | `insuniverse-server/apps/api/src/schedules/analysis-stall.schedule.ts:10` | ` * 자료수집완료(ODD002) / 자료분석중(ODD005) 상태가 1시간 경과되었고 최종분석상태 미박힘 인 경우` | 코드 없음 |
| `monitoring:analysis-status` | 10분 | `insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:265` | `      .subtract(10, 'minute')` | `insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:259` | `  /** 분석목록 생성일시가 현시간보다 5분이상 초과되었고` | 불일치 |
| `monitoring:analysis-status` | 5분 | — | — | `insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.service.ts:410` | `  //* 5분 경과 인증요청중/추가인증요청중 일괄 final_state=인증실패 처리 + 환불` | 코드 없음 |
| `monitoring:analysis-status` | 30분 | `insuniverse-server/apps/api/src/modules/treat/treat-alert.service.ts:6` | `const DEDUPE_MS = 30 * 60 * 1000; //* 같은 경고 30분 중복억제(스팸 방지)` | `insuniverse-server/apps/api/src/modules/treat/treat-alert.service.ts:6` | `const DEDUPE_MS = 30 * 60 * 1000; //* 같은 경고 30분 중복억제(스팸 방지)` | 일치 |
| `monitoring:analysis-status` | 5분 | — | — | `insuniverse-server/apps/api/src/schedules/analysis-status.schedule.ts:10` | ` * 동작주기 : 5분 (AWS EventBridge)` | 코드 없음 |
| `monitoring:fax-status` | 1시간 | `insuniverse-server/apps/api/src/modules/fax/fax.service.ts:1195` | `    const TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1시간` | `insuniverse-server/apps/api/src/schedules/fax-status.schedule.ts:12` | ` * - 3시간 초과 시: FAILED 처리 (응답 시간 초과)` | 불일치 |
| `monitoring:fax-status` | 1시간 | — | — | `insuniverse-server/apps/api/src/modules/fax/fax.service.ts:1173` | `   * 분기1: 1시간 초과 → 발송취소 요청` | 코드 없음 |
| `monitoring:fax-status` | 1분 | — | — | `insuniverse-server/apps/api/src/schedules/fax-status.schedule.ts:8` | ` * 동작주기 : 1분 (AWS EventBridge에서 설정)` | 코드 없음 |

**갈림 둘.**

*(가) 팩스 응답 대기.* 스케줄러 docblock 은 `3시간 초과 시 FAILED` 라고 적고 실제 임계 상수는
1시간이다. 같은 서비스 파일 안의 주석들은 전부 1시간을 말한다 — 즉 갈리는 것은 **스케줄러
docblock 한 줄과 서비스 쪽 다섯 줄**이다. 표는 그 한 줄과 상수를 한 행에 묶어 `불일치` 로,
서비스 쪽 주장을 따로 한 행으로 싣는다.

*(나) 인증 stale.* 리포지터리 주석은 `5분이상` 이라 적고 같은 메서드의 계산은 10분을 뺀다.
같은 오기가 서비스 쪽 주석에도 있다. 스케줄러 docblock 은 `일정시간 경과` 로 숫자를 적지 않아
그 자리에서는 갈림이 보이지 않는다.

**stall 축은 갈리지 않는다.** 리포지터리 주석과 계산이 모두 1시간이고 스케줄러 docblock 도
같다. 갈리지 않는 자리를 표에서 빼면 표본이 범위인 척하게 되므로 `일치` 행도 남김없이 싣는다.

`동작주기` 주장이 전부 `코드 없음` 인 것은 호출 주기가 저장소 밖(EventBridge)에서 정해지기
때문이다. 저장소는 그 주기를 주석으로만 말한다.

## 강제 지점 (개발)

규칙 내용을 다시 쓰지 않는다. 각 정책이 저장소 안 **어디에서** 강제되는가와 그 지점이 어떤
기제인가만 적는다. 배치가 부록인 정책도 이 절에 있다.

### monitoring:alimtalk-status

- 강제 방식: 외부 API 폴링. `insuniverse-server/apps/api/src/modules/alimtalk/alimtalk.service.ts:648` 이 완료 확인 대상을 고르고 기간 임계를 쓰지 않는다.
- 진입: `insuniverse-server/apps/api/src/schedules/alimtalk-status.schedule.ts:22` 가 대상 목록을 읽고 `:27` 이 건별로 발송사에 상태를 물어 갱신한다.
- 기준점: 동결 stg `4d798f55`.

### monitoring:analysis-stall

- 강제 방식: 리포지터리 조회 질의 + 상태 전이. `insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:292` 가 1시간 경계를 계산해 정체 건을 고른다.
- 진입: `insuniverse-server/apps/api/src/schedules/analysis-stall.schedule.ts:24` 가 정체 건 종결을, `:28` 이 예약수 정합 보정을 부른다.
- 기준점: 동결 stg `4d798f55`.

### monitoring:analysis-status

- 강제 방식: 리포지터리 조회 질의 + 상태 전이 + 알림 발송. `insuniverse-server/apps/api/src/modules/order/order-detail/order-detail.repository.ts:265` 가 임계를 계산하고, `insuniverse-server/apps/api/src/modules/treat/treat-alert.service.ts:6` 이 같은 경고의 재발송 억제 창을 정한다.
- 진입: `insuniverse-server/apps/api/src/schedules/analysis-status.schedule.ts:25` · `:29` · `:33` 세 호출이 인증 stale 정산 · 예약수 정합 보정 · 모니터링 경고 발송을 차례로 부른다.
- 기준점: 동결 stg `4d798f55`.

### monitoring:fax-status

- 강제 방식: 외부 API 폴링 + 타임아웃 분기. `insuniverse-server/apps/api/src/modules/fax/fax.service.ts:1195` 의 상수가 대기 임계를 정하고 그 값으로 취소 요청과 상태 조회가 갈린다.
- 진입: `insuniverse-server/apps/api/src/schedules/fax-status.schedule.ts:28` 이 유일한 호출이며 `try`/`catch` 안에 있다.
- 기준점: 동결 stg `4d798f55`.

## 적용 범위 (actor × 서비스)

감시 규칙에는 actor 표면이 없다. 상태 전이의 *결과* 는 여러 actor 에게 보이지만 이 문서의 정책은
**서버가 임계를 넘긴 건을 고르는 조건**에 대한 사실이며 그 조건은 어느 actor 의 요청 경로에도
등장하지 않는다. actor 다섯 열이 전건 `—` 인 것은 빈 칸이 아니라 관측된 부재다. 서비스 축에서는
`server` 하나만 `적용` 이다.

| 정책 id | 관리자 | 영업대리점 | 기업담당자 | 일반회원 | 기업회원 | server | site | admin |
|---|---|---|---|---|---|---|---|---|
| monitoring:alimtalk-status | — | — | — | — | — | 적용 | — | — |
| monitoring:analysis-stall | — | — | — | — | — | 적용 | — | — |
| monitoring:analysis-status | — | — | — | — | — | 적용 | — | — |
| monitoring:fax-status | — | — | — | — | — | 적용 | — | — |

## 예외·폴백

예외가 없는 정책도 행을 둔다 — 없다는 것도 확인의 결과이며 그 확인 근거를 함께 적는다.

| 정책 id | 예외 | 폴백 | 확인 근거 |
|---|---|---|---|
| monitoring:alimtalk-status | 발송사 식별값이 없는 건은 테스트 모드로 보고 대상에서 뺀다 | 건별 갱신 실패를 로그로만 남기고 다음 건을 계속 처리한다 (`apps/api/src/schedules/alimtalk-status.schedule.ts:29`~`:31`) | 반복문 안 `catch` 가 오류를 삼킨다. 실패한 건은 다음 실행에서 다시 대상이 된다 |
| monitoring:analysis-stall | 최종분석상태가 이미 박힌 건은 대상에서 뺀다 | 관측된 폴백 없음 | 1회 가드 마커로 중복 처리를 막는다. 최상위 두 호출에 `catch` 절이 없다 |
| monitoring:analysis-status | 어드민이 켜지 않은 경고 종류는 발송하지 않는다. 같은 경고는 억제 창 안에서 다시 보내지 않는다 | 셋째 동작(경고 발송)이 던지면 그 오류를 로그로 남기고 발송 0건으로 처리해 배치를 계속 진행한다 (`apps/api/src/schedules/analysis-status.schedule.ts:36`~`:39`) | 셋째 호출만 `catch` 를 갖는다 — 앞의 두 동작은 던지면 그 실행이 거기서 끝난다 |
| monitoring:fax-status | 팩스 식별값이 없는 건은 건너뛴다. 취소 요청이 이미 발송됨으로 거부되면 상태 조회로 갈아탄다 | 배치 전체를 `try`/`catch` 로 감싸 실패를 로그로만 남긴다 (`apps/api/src/schedules/fax-status.schedule.ts:30`~`:32`) | 취소 실패의 두 갈래(이미 발송 · 기타)가 각각 다른 처리를 갖는다. 대기 상태로 남은 건은 다음 실행에서 재처리된다 |

## 부록

이 절의 정책은 **현행 발효 여부가 판정되지 않아** 본문에서 격리한다 — 판정에 필요한 입력
(EventBridge 규칙 · ECS task-def)이 저장소 밖에 있고, 분석 상태변경 축은 관리자 설정까지 저장소
밖이다. 그 상태로 본문에 두면 독자가 지금 걸려 있는 감시 규칙으로 읽는다.

**여기 실린 어떤 정책도 배치의 활성을 부정하지 않는다.** 미판정과 부정은 다르다. 저장소는
활성을 긍정할 근거도 부정할 근거도 갖고 있지 않다.

각 블록은 **규칙의 조건과 결과**를 코드 심볼 없이 적는다. 좌표는 프론트매터 `anchors` 와
`## 근거 대조표` 가 진다.

### monitoring:alimtalk-status

알림톡 발송요청이 성공으로 기록됐는데 발송 완료 여부가 아직 비어 있는 건을 골라, 발송사에
결과를 물어 완료 상태와 메시지를 채운다. **기간 임계가 없다** — 완료 칸이 비었는가만 본다.
발송사 식별값이 없는 건은 테스트 모드로 보고 대상에서 뺀다.

사용자에게 일어나는 일: 알림톡 목록의 발송 결과가 요청 성공에서 실제 완료·실패로 갱신된다.
갱신되기 전까지는 요청 성공 상태로 보인다.

발효 판정에 남은 입력은 `04-runtime-topology.md#schedule:alimtalk-status` 가 진다.

### monitoring:analysis-stall

자료수집완료 또는 자료분석중 상태로 **1시간**이 지났는데 최종 분석상태가 아직 박히지 않은 건을
골라 최종 분석상태를 분석실패로 박고 예약수를 환불한다. 원래의 진행 상태 값은 바꾸지 않는다 —
관리자 화면에서 원본 진행 이력을 보존하기 위해서다. 사용자와 관리자에게 보이는 것은 최종
상태가 우선이다.

같은 배치가 예약수 정합 보정도 함께 부른다. 환불이 새는 경우를 스스로 메우는 동작이다.

사용자에게 일어나는 일: 1시간 넘게 진행 중이던 분석이 실패로 정리되고 차감된 예약수가 돌아온다.
1회 가드 마커가 있어 같은 건이 두 번 환불되지 않는다.

발효 판정에 남은 입력은 `04-runtime-topology.md#schedule:analysis-stall` 이 진다.

### monitoring:analysis-status

인증요청중 또는 추가인증요청중 상태로 임계 시간이 지났는데 최종 분석상태가 아직 박히지 않은 건을
골라 최종 분석상태를 인증실패로 박고 예약수를 환불한다. stall 축과 마찬가지로 원래의 진행 상태
값은 바꾸지 않으며 1회 가드 마커로 중복 처리를 막는다.

**이 배치는 최상위에서 세 가지를 한다.**

1. 인증 stale 정산 — 위 규칙이다.
2. 예약수 정합 보정 — 환불 누수를 스스로 메운다. stall 배치도 같은 보정을 부르며, 여기서는 stale
   정산 직후에 실행해 정확한 값으로 맞춘다.
3. 모니터링 경고 발송 — 진료 모니터의 위험 경고를 채널로 보낸다. **어드민이 켠 종류만** 보내고,
   같은 경고는 **30분** 안에 다시 보내지 않으며, 경고가 사라지면 복구 알림을 한 번 보낸다.

셋째의 대상 선택은 **운영 DB 의 관리자 설정**에 걸려 있다. 저장소는 그 설정을 담지 않으므로 활성이
확인되더라도 실제 발송 여부는 그 데이터가 정한다. 정책 id 를 셋으로 쪼개지 않은 것은 조회와
처분을 가르는 기준이 저장소 안에 없기 때문이며, 세 동작의 좌표는 `anchors` 가 전건 담는다.

**임계에서 코드와 주석이 갈린다.** 주석 두 곳은 5분이라 적고 실제 계산은 10분을 뺀다. 스케줄러
docblock 은 숫자를 적지 않아 그 자리에서는 갈림이 보이지 않는다. 이 문서는 어느 쪽이 옳은지
판정하지 않는다 — 사용자 입장에서는 인증 대기가 실패로 정리되기까지의 시간이 5분인지 10분인지가
갈린 채 남아 있다는 뜻이다.

발효 판정에 남은 입력은 `04-runtime-topology.md#schedule:analysis-status` 가 진다.

### monitoring:fax-status

발송 상태가 대기인 팩스 건에 대해 발송사 API 로 결과를 물어 성공 또는 실패로 정리한다. 실패로
정리되면 차감된 머니를 되돌린다. 대기 시간이 임계를 넘긴 건은 결과를 묻기 전에 **발송 취소**를
먼저 요청하고, 취소가 성공하면 대기가 길어 취소된 것으로 기록한다. 취소가 이미 발송됨으로
거부되면 그 건은 상태 조회로 갈아탄다.

**임계에서 코드와 주석이 갈린다.** 스케줄러 docblock 은 3시간이라 적고 서비스의 실제 상수는
1시간이다. 서비스 파일 안의 주석은 모두 1시간을 말한다. 이 문서는 어느 쪽이 옳은지 판정하지
않는다 — 사용자 입장에서는 팩스가 대기 상태로 얼마나 오래 남아 있다가 실패로 정리되는지가
갈린 채 남아 있다는 뜻이다.

발효 판정에 남은 입력은 `04-runtime-topology.md#schedule:fax-status` 가 진다.

## 연혁

릴리스 태그가 존재하지 않는다. 릴리스 열은 `—` 로 두고 커밋 SHA 로 추적한다. 행은
`docs/_meta/index/commits.server.jsonl` 이 이 문서의 앵커 경로 접촉으로 기록한 커밋 집합 안에서
**감시 규칙이 바뀐 시점**만 골랐다. 그 집합 전체(199건)를 싣지 않는다 — 이 계수는 문서의
주장이 아니라 게이트 `#9b` 가 인덱스에서 다시 세는 값이다.

| 릴리스 | 날짜 | SHA | 변경 요약 |
|---|---|---|---|
| — | 2024-12-27 | `8e1366c0` | 분석 상태변경 배치 파일 신설 |
| — | 2024-12-30 | `aa9ce0d0` | 인증요청중 상태 전이 규칙 신설 |
| — | 2025-01-07 | `b852e6ce` | 알림톡 발송완료 확인 배치 신설 |
| — | 2025-04-17 | `3abbba06` | 인증 stale 임계를 10분으로 변경 — 주석은 5분에 머물렀다 |
| — | 2026-04-08 | `1c737042` | 팩스 결과 수신을 스케줄러로 교체 — 이 시점의 임계는 3시간 |
| — | 2026-04-13 | `efe6d58f` | 팩스 임계를 1시간으로 단축 — 스케줄러 docblock 은 3시간에 머물렀다 |
| — | 2026-05-22 | `3775e2cc` | 분석 stall 자동 종결 배치 신설 — 임계 1시간 |
| — | 2026-06-30 | `e910fca5` | 모니터링 경고 발송 신설 — 30분 억제 창 |

**두 갈림의 기원이 이 표에 있다.** 인증 stale 은 2025-04-17 에 코드만 10분으로 옮겨 갔고,
팩스는 2026-04-13 에 코드만 1시간으로 옮겨 갔다. 두 경우 모두 주석이 이전 값에 남았다. 이 표는
그 시점을 보이지만 어느 값이 의도였는지는 저장소가 말하지 않는다.
