Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/multitenancy-isolation/case/case-analysis-finding-a06-f004.md
T
DongHyeonkaandClaude Fable 5.1 b25357c48a docs(clean-architecture-backend-template): fold analysis into final and re-select one topic
- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다
- 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5
  (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를
  techviz 로 만들었다
- 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs
  돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다.
  Concept 이 인용한 코드가 SSOT 에 없어 뺐다
- candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +09:00

177 lines
20 KiB
Markdown

---
kind: CASE
slug: analysis-finding-a06-f004
title: 전용 category 를 붙이는 유일한 분기가 도달할 수 없다
topic: multitenancy-isolation
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: case:analysis-finding-a06-f004
evidenceCapturedOn: 2026-09-04
body: case-analysis-finding-a06-f004.body.md
assets:
- key: analysis-finding-a06-f004
file: ../../../final/evidence/rendered/analysis-finding-a06-f004.svg
evidence:
- ../../../final/evidence/raw/analysis-finding-a06-f004.txt
source:
- 원본 분석 절은 final/document.md#a06 §16 이다.
---
# 전용 category 를 붙이는 유일한 분기가 도달할 수 없다
`MongoFailureCategory:68``SCHEMA_VERSION_UNSUPPORTED` 가 있고 `MongoDataSchemaUnsupportedException:42`~`:53` 이 세 버전을 공개 접근자로 노출한다. 그 값을 쓰는 자리는 저장소 전체에 열거형 선언과 `DefaultMongoFailureTranslator:110``case` 둘뿐인데, 분류기가 그 값을 만들지 않으므로 그 `case` 는 돌지 않는다.
## 관계
- **모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다**
그 규칙은 한번 접힌 정보가 복구되지 않는다고 적는다. 여기서는 스키마 버전 거절이 `OPERATION_REJECTED` 로 접혀 다른 로컬 거절과 구별되지 않는다.
- **위험한 조합은 정책이 아니라 생성자가 거부하게 만든다**
그 규칙대로면 버전을 모르는 예외는 만들어질 수 없어야 한다. `MongoDataSchemaUnsupportedException` 의 생성자는 `-1` 셋을 그대로 받고, `DefaultMongoFailureTranslator:111` 이 그 값으로 예외를 만든다.
- **중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다**
같은 예외를 만드는 자리가 둘인데 조립 상태가 반대다. `DefaultMongoFailureTranslator``MongoPlatformAutoConfiguration:93` 이 빈으로 만들지만 그 `case` 가 실행되지 않고, `MongoSchemaVersionPolicy` 는 값을 아는 쪽인데 `new` 를 부르는 main 줄이 0 이다.
## 문제
이 실패에는 열거형 값 하나와 예외 타입 하나가 따로 마련돼 있다. 예외는 세 정수를 접근자로 내주므로 분기할 범주와 진단할 값을 함께 줄 수 있다.
그 어휘가 실제로 쓰이는지 확인했다.
## 결론
MongoFailureCategory:3~:9 는 이 값이 메트릭과 대시보드에 나타난다고 적는다. 그래서 알 수 없는 서버 코드는 새 범주를 만드는 대신 UNCLASSIFIED 로 간다.
new MongoDataSchemaUnsupportedException 을 부르는 main 자리는 둘이다.
MongoSchemaVersionPolicy:85 가 실제 버전 값 셋을 넘긴다. version.value() 와 range.minimumSupported().value() 와 range.current().value() 다.
그런데 그 예외가 들고 가는 context 는 :84 의 MongoFailureContext.rejected(...) 가 만든 것이다. MongoFailureContext:135~:148 이 그 안에 OPERATION_REJECTED 와 NOT_SENT 를 박는다. 전용 범주가 아니라 로컬 거절 범주다.
DefaultMongoFailureTranslator:111 이 전용 범주를 붙이는 유일한 자리인데 세 값 모두 -1 이다.
그런데 그 분기에 들어올 값이 없다. SCHEMA_VERSION_UNSUPPORTED 라는 이름이 저장소 전체에 두 줄만 있고 그 둘이 방금 인용한 선언과 case 이므로, 그 값을 컨텍스트에 넣는 코드가 존재하지 않는다. 같은 검색을 OPERATION_REJECTED 로 걸면 넷이 나온다.
분류기가 범주를 정하는 자리는 DefaultMongoFailureClassifier 안에 여럿이다. :88~:96 이 드라이버 라벨을 먼저 보고, :99 와 :106~:109 와 :112 가 단계와 결과에 따라 고르며, 마지막이 :156 의 CATEGORY_BY_CODE 조회다. 어느 자리에서도 이 범주가 나오지 않는다.
그래서 SCHEMA_VERSION_UNSUPPORTED 로 집계되는 실패가 생기지 않는다. 다른 범주로 새는 것도 아직 없다. new MongoSchemaVersionPolicy 를 부르는 main 줄이 0 이고 그 이름이 main 에 나오는 자리가 자기 선언 셋뿐이라, 이 리비전에서는 그 예외를 던지는 코드가 돌지 않는다. 시험만 여섯 번 만든다.
MongoSchemaVersionPolicyTest 가 그 어긋남을 잡지 못한다. 시험 여섯이 전부 던져진 타입이나 반환값만 보고, 범주와 세 접근자를 확인하는 줄이 하나도 없다.
그래서 이 결함은 채택자가 정책을 배선하는 순간에 드러난다. 전용 칸이 비어 있는 채로 거절만 다른 칸에 쌓이기 시작한다.
이 모듈에 살아 있는 다른 스키마 실패 경로는 세 번째 범주를 쓴다. MongoFailureContext.schemaMismatch:157 이 SCHEMA_VALIDATION 과 NO_WRITE_PERFORMED 를 박고, PolicyAwareMongoTypeMapper:112·:185·:192 가 그것을 부른다. 저장된 문서가 이 릴리스가 기대하는 스키마와 맞지 않을 때 실제로 도는 것이 그 경로다.
-1 을 채워 넣는 자리가 하나 더 있다. DefaultMongoFailureTranslator:106 의 MongoDocumentTooLargeException 인데, 이쪽은 서버 코드 10334 와 17420 이 그 범주로 매핑돼 있어 실제로 만들어진다. 접근자 계약에 값이 없을 수 있다는 말이 없는 것만 남는다.
## 검증 환경
OpenJDK : 21.0.12
확인 방식 : 범주 열거형의 클래스 자바독과 두 상수 인용, 전용 예외 전문 인용, 프로덕션 생성 지점 전수, 정책이 만드는 context 와 그 팩터리가 박는 값 인용, translator 의 switch 전문 인용, 전용 범주가 나오는 줄의 저장소 전체 계수와 대조 계수, 분류기가 범주를 정하는 자리 전부 인용, 같은 모듈의 다른 스키마 실패 팩터리와 그 호출자 인용, 두 생성 지점의 조립 계수와 대조, 정책 시험의 단언 전수와 접근자 단언 계수와 대조
소스 수정 : x
## 재현 조건
1. 범주 열거형의 클래스 자바독과 이 값의 주석을 인용한다.
2. 전용 예외의 자바독과 세 접근자를 인용한다.
3. new MongoDataSchemaUnsupportedException 을 부르는 main 자리를 전부 찾는다.
4. 정책이 만드는 context 와 그 팩터리 메서드 본문을 인용한다.
5. translator 의 switch 를 전문으로 싣는다.
6. 전용 범주가 저장소 전체에 몇 줄 나오는지 세고, 다른 범주 이름으로 같은 검색을 건다.
7. 분류기가 범주를 정하는 자리를 전부 인용하고, 같은 모듈의 다른 스키마 실패 팩터리와 그 호출자를 함께 싣는다.
8. 정책 시험의 단언을 전부 뽑고 접근자를 단언하는 줄을 센다.
## 본문
<!-- body:start -->
`MongoFailureCategory` 는 실패의 안정된 부모 범주다. 스키마 버전 실패를 위한 값이 그 안에 있다.
## 이 실패에 마련된 값과 타입
:::evidence key="analysis-finding-a06-f004" alt="저장소 루트에서 돌린 정적 검색 출력 288줄. 먼저 MongoFailureCategory 3~10번 클래스 자바독이 실리는데 서버 코드와 드라이버 오류 라벨은 릴리스마다 바뀌지만 이 범주는 안 바뀌며 메트릭과 대시보드에 나타나는 값이 이것이고, 그래서 알 수 없는 서버 코드가 런타임에 새 범주를 만드는 대신 UNCLASSIFIED 로 간다고 적는다. 63~72번에 OPERATION_REJECTED 와 SCHEMA_VERSION_UNSUPPORTED 와 UNCLASSIFIED 세 상수가 각자 주석과 함께 나오는데, 68번의 주석은 저장된 문서의 스키마 버전이 지원 범위 밖이라는 것이다. 이어서 MongoDataSchemaUnsupportedException 5~11번 자바독이 실리는데 도메인 역직렬화 전에 올라오는 것이 이 예외의 가치이고 새 릴리스가 쓴 문서나 은퇴한 버전이 남긴 문서를 현재 모양으로 조용히 밀어 넣으면 안 되며 버전은 정수라 메시지에 이름을 적어도 안전하다고 적는다. 22~39번 생성자가 세 정수를 요구하고 stored schema version 뒤에 문서 버전과 지원 범위를 이어 붙인 메시지를 만들며, 41~54번이 documentVersion 과 minimumSupported 와 currentVersion 세 공개 접근자다. 다음으로 new MongoDataSchemaUnsupportedException 을 부르는 main 자리가 둘 나오는데 MongoSchemaVersionPolicy 85번과 DefaultMongoFailureTranslator 111번이다. 그 아래 MongoSchemaVersionPolicy 82~87번이 실리는데 83~84번이 MongoFailureContext.rejected 로 context 를 만들고 85~86번이 version.value 와 range.minimumSupported().value 와 range.current().value 를 넘긴다. 이어서 MongoFailureContext 135~149번의 rejected 팩터리가 실리는데 140번이 MongoFailureCategory.OPERATION_REJECTED 를, 141번이 MongoExecutionOutcome.NOT_SENT 를 박는다. 다음으로 DefaultMongoFailureTranslator 88~118번이 실린다. 91번이 classification.category() 로 switch 를 열고 106번이 MongoDocumentTooLargeException 을 -1L 과 -1L 로, 110~111번이 MongoDataSchemaUnsupportedException 을 -1 과 -1 과 -1 로 만들며, 112~114번 주석이 벌크 부분 실패와 로컬 거절은 그 세부를 소유한 계층이 올리는 것이라 그 범주를 달고 번역까지 온 실패는 이 릴리스가 알아보지 못하는 것이라고 적고 115~116번이 그 셋을 MongoUnclassifiedFailureException 으로 보낸다. 이어서 SCHEMA_VERSION_UNSUPPORTED 가 저장소 전체에 나오는 줄이 2 개라고 나오고 그 둘이 MongoFailureCategory 68번의 선언과 DefaultMongoFailureTranslator 110번의 case 로 나열되며, 대조로 센 OPERATION_REJECTED 는 4 개다. 그 아래 DefaultMongoFailureClassifier 24~52번의 CATEGORY_BY_CODE 지도가 실리는데 서버 코드 11000 부터 31 까지 스물여섯 항목이 DUPLICATE_KEY 와 SCHEMA_VALIDATION 과 WRITE_CONFLICT 와 WRITE_CONCERN 과 READ_CONCERN 과 SERVER_SELECTION 과 CONNECTION 과 TIMEOUT 과 CURSOR 와 DOCUMENT_TOO_LARGE 와 SHARD_ROUTING 과 RESUME 과 ENCRYPTION 으로 가고 스키마 버전 항목이 없다. 이어서 같은 파일 86~120번이 실리는데 86~87번 주석이 라벨을 먼저 보는 이유가 서버 자신의 복구 가능성 진술이 어떤 코드 표보다 우선하기 때문이라고 적고, 88~89번과 91~92번이 커밋 미상과 일시적 트랜잭션 라벨을 각각 처리하며, 94~96번이 쓰기 없음 라벨을, 99번이 categoryOf 를, 101~109번이 아무것도 보내지 않은 단계에서 UNCLASSIFIED 를 SERVER_SELECTION 으로 바꾸는 것을, 111~112번이 커밋 단계의 미상 결과를 처리한다. 150~157번의 categoryOf 는 서버 코드가 없으면 재시도 가능 쓰기 라벨 여부로 CONNECTION 이나 UNCLASSIFIED 를 돌려주고 있으면 그 지도를 조회하며 없으면 UNCLASSIFIED 다. 그 아래 같은 정규식을 MongoFailureContextTest 에 걸면 1 개가 나온다는 대조가 있다. 다음으로 MongoFailureContext 151~172번의 schemaMismatch 팩터리가 실린다. 152~155번 자바독은 저장된 문서가 이 릴리스가 기대하는 스키마와 맞지 않는 경우이고 쓴 것이 없지만 읽기가 서버에 닿았으며 돌아온 것이 쓸 수 없는 값이라 결과가 NOT_SENT 가 아니라 NO_WRITE_PERFORMED 라고 적으며, 162번이 SCHEMA_VALIDATION 을 163번이 NO_WRITE_PERFORMED 를 박는다. 그 팩터리를 부르는 자리는 PolicyAwareMongoTypeMapper 112번과 185번과 192번이다. 이어서 new MongoSchemaVersionPolicy 를 부르는 main 줄이 0 개라고 나오고 그 타입이 main 에 나오는 자리가 MongoSchemaVersionPolicy 17번의 클래스 선언과 22번과 29번의 두 생성자 셋뿐이며, 대조로 센 시험 쪽 생성은 6 개다. DefaultMongoFailureTranslator 를 빈으로 만드는 main 줄은 2 개인데 MongoPlatformAutoConfiguration 93번과 DefaultMongoFailureTranslator 47번의 정적 팩터리다. 다음으로 MongoDocumentTooLargeException 26~36번이 실리는데 28번 자바독이 추정 직렬화 크기이며 내용이 아니라 크기라 로그에 남겨도 안전하다고만 적고, 33번은 초과된 상한이라고만 적는다. 마지막으로 MongoSchemaVersionPolicyTest 의 시험 여섯과 그 단언이 나오는데 21번과 31번과 45번이 isInstanceOf(MongoDataSchemaUnsupportedException.class) 이고 나머지는 resolveMissingVersion 과 writeVersion 과 requiresReadTimeConversion 과 음수 버전 거절을 본다. 그 시험이 category 나 세 버전 접근자를 단언하는 줄은 0 개다." caption="범주 열거형의 대시보드 자바독과 두 상수 · 전용 예외의 자바독과 세 접근자 · 프로덕션 생성 지점 둘 · 정책이 만드는 context 와 rejected 팩터리가 박는 값 · translator 의 switch 전문 · 그 범주가 저장소 전체에 두 줄뿐인 것과 분류기의 범주 지도 · 두 생성 지점의 조립 상태 · 같은 모양의 DocumentTooLarge · 정책 시험의 단언과 접근자 단언 0 — 288줄 · exit 0" zoom="true"
:::
`:3`\~`:9` 클래스 자바독이 이 열거형의 용도를 적는다. 릴리스마다 바뀌는 서버 코드와 드라이버 라벨 위에 얹는 고정된 값이고, 알 수 없는 서버 코드가 런타임에 새 범주를 만들어 메트릭 카디널리티를 터뜨리는 대신 `UNCLASSIFIED` 로 가는 이유가 그것이다.
`:68``SCHEMA_VERSION_UNSUPPORTED` 는 저장된 문서의 스키마 버전이 지원 범위 밖이라는 뜻이다. 바로 위 `:65``OPERATION_REJECTED` 는 플랫폼이 서버에 닿기 전에 로컬에서 거절했다는 뜻이다.
`MongoDataSchemaUnsupportedException:5`\~`:11` 은 이 예외가 도메인 역직렬화 전에 올라오는 것이 핵심이라고 적는다. 버전은 정수이고 데이터를 담지 않아서 메시지에 이름을 적어도 안전하다는 것도 함께 있다.
`:22`\~`:39` 의 생성자가 그 셋을 요구하고 메시지를 조립한다. `:42`·`:47`·`:52``documentVersion()``minimumSupported()``currentVersion()` 이다. 분기할 범주와 진단할 값이 둘 다 준비돼 있다.
## 생성 지점 둘
`new MongoDataSchemaUnsupportedException` 을 부르는 main 자리는 `MongoSchemaVersionPolicy:85``DefaultMongoFailureTranslator:111` 둘이다.
## MongoSchemaVersionPolicy 가 넘기는 context 의 범주
`MongoSchemaVersionPolicy:85`\~`:86``version.value()``range.minimumSupported().value()``range.current().value()` 를 넘긴다. 진짜 값이다.
그런데 `:83`\~`:84` 가 그 예외에 넣을 context 를 `MongoFailureContext.rejected(new MongoOperationName("schema.version-check"))` 로 만든다.
`MongoFailureContext:135`\~`:148` 이 그 팩터리다. `:140` 이 범주를 `OPERATION_REJECTED` 로, `:141` 이 결과를 `NOT_SENT` 로 박는다. 인자로 받는 것은 연산 이름 하나뿐이라 다른 범주를 넣을 자리가 없다.
## DefaultMongoFailureTranslator\:111 이 넣는 -1, -1, -1
`DefaultMongoFailureTranslator:91``classification.category()``switch` 를 연다. `:110`\~`:111` 이 그 범주를 받으면 `MongoDataSchemaUnsupportedException``-1, -1, -1` 로 만든다.
여기서는 버전을 알 수 없다. 드라이버가 보고한 실패에서 문서의 스키마 버전을 읽을 방법이 없기 때문이다.
## 그 case 에 들어오는 값이 없다
`SCHEMA_VERSION_UNSUPPORTED` 가 저장소 전체에 나오는 줄은 둘이다. `MongoFailureCategory:68` 의 선언과 `DefaultMongoFailureTranslator:110``case` 다. `OPERATION_REJECTED` 로 같은 검색을 걸면 넷이 나온다. 2 는 pathspec 이 아무것도 훑지 못해 나온 값이 아니다.
분류기가 범주를 정하는 자리는 하나가 아니다. `:88`\~`:89``:91`\~`:92` 가 드라이버 라벨을 먼저 보고 커밋 미상이나 일시적 트랜잭션으로 보내고, `:94`\~`:96` 이 쓰기 없음 라벨을 처리하며, `:99``categoryOf` 를 부르고, `:106`\~`:109` 가 아무것도 보내지 않은 단계에서 `UNCLASSIFIED``SERVER_SELECTION` 으로 바꾸고, `:112` 가 커밋 단계의 미상 결과를 처리한다.
마지막이 `:150`\~`:157``categoryOf` 다. 서버 코드가 없으면 재시도 가능 쓰기 라벨 여부로 `CONNECTION` 이나 `UNCLASSIFIED` 를 주고, 있으면 `CATEGORY_BY_CODE` 를 조회한다. 그 지도 `:27`\~`:52` 의 스물여섯 항목이 열세 범주로 나뉘는데 스키마 버전이 없다.
어느 자리에서도 `SCHEMA_VERSION_UNSUPPORTED` 가 나오지 않는다. 그 이름이 저장소 전체에 두 줄뿐이라는 계수가 그것을 한 번에 말한다.
그래서 `classification.category()``SCHEMA_VERSION_UNSUPPORTED` 인 경우가 생기지 않고, `-1, -1, -1` 예외는 만들어지지 않는다.
## 두 생성 지점의 조립 상태가 반대다
`DefaultMongoFailureTranslator` 는 빈이다. `MongoPlatformAutoConfiguration:93` 이 분류기를 넣어 만들고, `DefaultMongoFailureTranslator:47` 에 기본 분류기를 쓰는 정적 팩터리도 있다. 조립돼 있는데 문제의 분기만 죽었다.
`MongoSchemaVersionPolicy` 는 반대다. `new MongoSchemaVersionPolicy` 를 부르는 main 줄이 0 이고, 그 이름이 main 에 나오는 자리는 `:17` 의 클래스 선언과 `:22`·`:29` 의 두 생성자뿐이다. 같은 검색을 시험에 걸면 여섯이 나온다.
그래서 이 리비전에서는 세 버전을 아는 예외가 아예 던져지지 않는다. 대시보드의 `SCHEMA_VERSION_UNSUPPORTED` 칸이 비는 것은 맞지만, `OPERATION_REJECTED` 칸으로 새는 것도 아직 없다.
이 정책은 채택자가 쓰라고 공개된 타입이다. 배선하는 순간 스키마 버전 거절이 로컬 가드레일 거절과 같은 칸에 들어가고, 전용 칸은 계속 비어 있다.
`MongoSchemaVersionPolicyTest``:21`·`:31`·`:45``isInstanceOf` 로 예외 타입만 확인한다. 나머지 시험은 `resolveMissingVersion``writeVersion``requiresReadTimeConversion` 과 음수 버전 거절을 본다. `category()` 나 세 접근자가 나오는 줄이 하나도 없다.
같은 정규식을 `MongoFailureContextTest` 에 걸면 1 이 나온다. 0 은 그 정규식이 아무것도 못 잡아서 나온 값이 아니다.
## 살아 있는 스키마 실패는 세 번째 범주로 간다
`MongoFailureContext:151`\~`:171``schemaMismatch` 팩터리가 있다. `:162``SCHEMA_VALIDATION` 을, `:163``NO_WRITE_PERFORMED` 를 박는다. `:154`\~`:155` 자바독이 그 결과를 고른 이유를 적는다 — 쓴 것이 없지만 읽기는 서버에 닿았고 돌아온 것이 쓸 수 없는 값이라는 것이다.
부르는 자리는 `PolicyAwareMongoTypeMapper:112`·`:185`·`:192` 셋이다. 타입 매퍼가 문서를 도메인 타입으로 옮기다 실패할 때 도는 경로다.
그래서 이 모듈의 스키마 관련 실패는 세 범주로 갈린다. 타입 매퍼가 쓰는 `SCHEMA_VALIDATION`, 조립되지 않은 정책이 쓰는 `OPERATION_REJECTED`, 그리고 아무도 만들지 않는 `SCHEMA_VERSION_UNSUPPORTED` 다.
## -1 을 넣는 다른 예외 하나
`DefaultMongoFailureTranslator:106``MongoDocumentTooLargeException``-1L, -1L` 로 만든다.
`MongoDocumentTooLargeException:28` 은 그 값을 추정 직렬화 크기라고, `:33` 은 초과된 상한이라고 적는다. 값이 없을 수 있다는 말은 없다.
이쪽은 도달 가능하다. `CATEGORY_BY_CODE:46`\~`:47` 이 서버 코드 10334 와 17420 을 `DOCUMENT_TOO_LARGE` 로 보낸다. 드라이버가 보고한 실패에서 두 수를 알 수 없는 것은 어쩔 수 없지만, 접근자 계약에 그 사실이 없다.
## 원문과 갈리는 자리
원문은 생성 지점이 둘이고 각각 반쪽만 맞다고 적으면서, 전용 범주 칸이 세 버전이 `-1` 인 실패만 받는다고 적는다.
확인 결과는 다르다. 분류기가 그 범주를 만들지 않으므로 `-1` 예외조차 생기지 않고, `DefaultMongoFailureTranslator:110`\~`:111` 에는 값이 들어오지 않는다. 원문이 제안한 수정 — 정책이 전용 범주를 가진 context 를 만들면 translator 쪽 `-1` 경로를 정리할 수 있다 — 은 그대로 맞고, 정리 대상이 이미 실행되지 않는 `case` 라는 점만 다르다.
## 확인하지 못한 것
다른 분류기 구현이 조립될 수 있는지는 조립 경로로 따지지 않았다. 도달 불가라는 판정은 이름 계수 하나에 기대고 있다.
이 범주 이름을 소비하는 대시보드나 메트릭 설정은 저장소 밖에 있어 보지 않았다.
`MongoSchemaVersionPolicy` 가 던진 예외를 잡는 호출자가 세 접근자를 읽어 진단에 쓰는지 추적하지 않았다.
<!-- body:end -->