- 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>
11 KiB
kind, slug, title, topic, project, status, sourceRevision, evidenceCapturedOn, rootTreeNode, body, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | evidenceCapturedOn | rootTreeNode | body | assets | evidence | source | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | a14-f018-webplatformstartupvalidator | 운영 런북이 시작 검증기의 판정을 근거로 삼는데 그 검증기를 부르는 곳이 없다 | web-inbound-and-http-surface | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | 2026-09-03 | case:a14-f018-webplatformstartupvalidator | case-a14-f018-webplatformstartupvalidator.body.md |
|
|
|
운영 런북이 시작 검증기의 판정을 근거로 삼는데 그 검증기를 부르는 곳이 없다
플랫폼 시작 검증기가 선언된 통제 중 배선되지 않은 것이 있으면 기동을 거부하도록 만들어져 있다. 부르는 곳이 없다. 그런데 운영 런북이 그 검증기가 돈다는 전제로 장애 조사 첫 단계를 적어 두었다.
관계
- 라우트 게이트가 실물을 읽어 오는 한 조각만 아무도 만들지 않는다 원문이 §44.1·§44.2 로 나란히 적은 짝이다. 저쪽은 수집기가 생성되지 않고 여기는 검증기가 시작 훅에 걸리지 않는다.
- @Bean이 있다는 것은 조립 증거가 아니다 거기서는 등록된 빈이 조립을 뜻하지 않는다고 적었다. 여기는 그보다 앞이다. 클래스에 스테레오타입이 없어 스캔에도 걸리지 않는다.
- 시작 검증기가 도는지는 그 능력에 자동설정 루트가 있는지와 일치한다
이 리프에 그 루트가 없다. 자동설정 진입 목록이
mvc와webflux뿐이고admin은 없다. - 그러나 R0 경계가 문서에만 있고 compile 경로에서 닫히지 않는다 둘 다 문서가 코드보다 앞서 나갔다. 저쪽은 경계 서술이고 여기는 장애 대응 절차다.
문제
플랫폼에 시작 검증기가 있고 이름이 실행 시점을 약속한다. 부르는 곳을 확인했다.
결론
없다.
admin/platform 의 두 파일 중 main 에서 쓰이는 것은 스냅숏 하나뿐이고, 그것을 쓰는 곳도 검증기다. 검증기를 만드는 다섯 자리는 모두 시험이다.
스테레오타입도 없다. 출하보다 허용적인 스캔을 걸어도 두 클래스는 빈이 되지 않는다. 리프의 자동설정 진입점 둘도 mvc 와 webflux 뿐이라 admin 을 올리지 않는다.
src 에서 *StartupValidator 로 선언된 클래스를 모두 찾으면 여덟이고, 넷은 자기 파일 밖 main 에 이름이 나오고 넷은 나오지 않는다. 나오는 넷 중 파일서버·HTTP 클라이언트·GraphQL 플랫폼은 app-bootstrap 이나 자기 리프 자동설정이 빈으로 걸었고, Mongo 는 기동 검사 빈이 안에서 직접 만들어 부른다. 나오지 않는 넷은 웹 플랫폼·웹소켓 플랫폼·gRPC 플랫폼·gRPC 서블릿이다.
범위를 이름에서 런북으로 옮기면 더 많이 나온다. 런북들이 백틱으로 지목한 이름 중 main 에 같은 이름의 파일이 있으면서 자기 파일 밖 main 참조가 0 인 것이 열하나다. 그중 둘이 이 리프 런북에 있고, 둘 다 같은 추론 형태다. :27 이 이 검증기를 그렇게 쓰고, :89 가 WebCorsPolicyValidator 를 같은 문장 구조로 쓴다. 코퍼스가 그 두 번째 건은 이미 따로 적어 두었다.
원문에 없는 대목은 여기다. docs/web/runbook.md:27 이 이 검증기를 장애 조사 근거로 쓴다. 검증기가 돌아야 성립하는 추론이고, 돌지 않으므로 그 결론은 근거를 잃는다.
검증기 자체는 동작한다. 통제가 빠진 스냅숏에는 그 이름을 들어 거부하고 다 채워진 스냅숏은 통과시킨다.
원문이 인용한 "Better to refuse to start" 는 NginxInternalUriMapper:30 의 attestMapping() 자바독이다. 그 메서드를 파일서버 기동 검사가 FileserverStartupConfiguration:87 에서 부르므로 인용은 맞다.
판정은 P3 이고 원문과 같다.
검증 환경
OpenJDK : 21.0.12 Spring Boot : 4.0.8 확인 방식 : 두 클래스의 이름을 src 전체 자바 파일에서 전수 검색, 리프 자동설정 진입점과 스테레오타입 확인, 같은 스캔을 건 컨텍스트에서 빈 수 확인, 검증기를 두 스냅숏에 직접 실행, 운영 런북 두 편 대조, src 의 *StartupValidator 선언을 검색으로 모아 main 참조 수 비교, 원문이 인용한 문장의 실제 자리와 그것을 부르는 코드 확인 소스 수정 : x
재현 조건
원문 근거는 분석 문서의 #L1500 절이다.
- 검증기 자바독을 읽어 이름이 약속하는 시점과 거부 형태를 확인한다.
- admin/platform 두 클래스를 자기 파일 밖에서 쓰는 자리를 main 과 test 로 갈라 센다.
- 리프의 자동설정 진입 목록과 두 클래스의 스테레오타입 유무를 본다.
- 출하 루트가 이 리프에 거는 것과 같은 스캔을 admin 에 걸고 두 타입의 빈 수를 센다.
- app-bootstrap 이 다른 시작 검증기를 거는 형태를 읽고, 원문이 인용한 근거 문장이 어느 파일에 있고 무엇이 그것을 부르는지 확인한다.
- 검증기를 직접 만들어 통제가 빠진 스냅숏과 다 채워진 스냅숏에 각각 넣는다.
- 운영 런북에서 이 검증기를 근거로 쓰는 대목을 읽는다.
- src 에 선언된 *StartupValidator 를 모두 찾아 각각 자기 파일 밖 main 파일 수를 센다.
- 런북들이 백틱으로 지목한 이름을 모아, main 에 같은 이름의 파일이 있으면서 자기 파일 밖 main 참조가 0 인 것을 고른다.
본문
검증기 자바독이 실행 시점과 거부 형태를 함께 적어 두었다. 기동 시점에 보는 이유는 그러지 않으면 장애로 알게 되기 때문이고, 경고가 아니라 거부인 이유는 경고를 남기는 검증기는 아무도 읽지 않기 때문이다.
두 클래스를 쓰는 자리는 시험뿐이다
:::evidence key="a14-f018-webplatformstartupvalidator" alt="저장소 루트에서 돌린 정적 검색 출력 84줄. 검증기 자바독의 실행 시점과 거부 근거, admin/platform 두 클래스를 자기 파일 밖에서 쓰는 자리가 main 과 test 로 갈려 나오고 검증기 쪽은 test 뿐이다. 이어서 운영 런북이 이 검증기를 근거로 쓰는 세 줄, app-bootstrap 이 파일서버와 HTTP 클라이언트 시작 검증기를 빈으로 거는 두 자리, 이 리프의 자동설정 진입 목록 둘과 그 목록에서 admin 이라는 문자열을 담은 항목 수, 검증기 클래스의 스프링 스테레오타입 수, src 에 선언된 *StartupValidator 여덟과 각각의 이름이 자기 파일 밖 main 자바 파일 몇 개에 나오는지가 차례로 보인다. 자바독 언급도 그 수에 들어간다. 끝으로 런북들이 백틱으로 지목한 이름 중 main 참조가 0 인 열하나와, 그중 이 리프 런북이 같은 추론을 거는 두 자리가 나온다. 원문이 인용한 근거 주석과 그것을 부르는 기동 검사 자리도 그 사이에 있다." caption="admin/platform 두 클래스의 참조와 런북·형제 배선·검증기 여덟·런북 지목 식별자 대조 — 84줄 · exit 0" zoom="true"
:::
검증기 이름이 자기 파일 밖에 나오는 여섯 줄이 모두 그 시험 파일이다. 그중 다섯이 생성이고 하나는 시험 클래스 자신의 선언이다. 스냅숏은 main 에서 검증기 하나가 쓴다.
리프의 자동설정 진입점은 mvc 와 webflux 둘이고 admin 을 올리는 항목은 없다. 두 클래스에 스프링 스테레오타입도 붙어 있지 않다.
같은 저장소가 이미 쓰는 배선 형태가 있다
// FileserverStartupConfiguration.java:35-37 · :47-49
@Bean
@ConditionalOnMissingBean
FileserverStartupValidator fileserverStartupValidator() {
...
@Bean
FileserverStartupCheck fileserverStartupCheck(
FileserverStartupValidator validator,
검증기를 빈으로 만들고, 그것을 인자로 받는 두 번째 빈이 기동 시점에 실제 검사를 돌린다. HTTP 클라이언트 쪽도 같은 모양이다. 플랫폼 쪽에는 둘 다 없다.
런북이 이 검증기를 근거로 쓴다
docs/web/runbook.md:27-29
- **Check first:** the platform snapshot's `uninstalledControls()`. `WebPlatformStartupValidator`
fails startup on a missing required control, so a running instance with one missing means it was
not in the required list.
통제가 설정만 되고 배선되지 않은 장애를 다루는 절이다. 조사자에게 미설치 통제를 먼저 보라고 하고, 인스턴스가 떠 있다는 사실 자체를 근거로 삼아 그 통제는 필수 목록에 없었다고 결론짓게 한다.
검증기가 돌아야 성립하는 추론이다. 돌지 않으므로 누락된 통제가 필수 목록에 있었더라도 인스턴스는 그대로 떠 있고, 조사자는 반대 결론에 이른다.
넣어 보면 거부한다
:::evidence key="a14-f018-webplatformstartupvalidator-run" alt="JVM 프로브 출력 14줄. 통제 셋 중 하나가 배선되지 않은 스냅숏을 넣었을 때 스냅숏이 보고하는 미설치 통제 이름과 검증기가 던진 예외 이름과 메시지, 셋 다 배선된 스냅숏을 넣었을 때 통과한 결과, 그리고 출하 루트의 기본 패키지를 제외 필터 없이 admin 에 걸었을 때 올라온 두 타입의 빈 수가 각각 0 이라는 결과가 보인다." caption="검증기를 두 스냅숏에 직접 넣고, 같은 스캔에서 빈 수를 센 결과 — 14줄 · exit 0" zoom="true" :::
통제 하나가 빠진 스냅숏을 넣으면 그 이름을 들어 거부한다. 거부 메시지가 런북과 같은 말을 한다. 설정만 되고 설치되지 않은 통제는 필요해지는 순간까지 동작하는 것과 구별되지 않으므로 여기서 기동을 실패시킨다는 것이다.
셋 다 배선된 스냅숏은 통과한다. 같은 프로브가 출하 루트의 기본 패키지를 제외 필터 없이 admin 에 걸어 보면 두 타입 다 빈이 0 개다.
확인하지 못한 것
애플리케이션을 통째로 띄워 검증기가 돌지 않는 것을 확인하지 않았다. 확인한 것은 main 에 만드는 곳이 없고 스캔으로도 올라오지 않는다는 것까지다.
런북이 첫 단계로 지시하는 스냅숏 조회를 실제로 해 보지 않았다. 확인한 것은 main 에서 스냅숏을 쓰는 곳이 검증기 하나뿐이라는 것까지다.
프로브가 넣은 통제 이름 셋은 지어낸 것이다. 실제 배포가 어떤 이름을 필수로 두는지는 확인하지 않았다.