refactor: 문서 개선 중

This commit is contained in:
donghyeon-ka
2026-09-21 14:30:55 +09:00
parent c93cdea150
commit 805a18f486
1497 changed files with 525837 additions and 59152 deletions
@@ -7,9 +7,9 @@
"revision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916",
"verified": "git rev-parse HEAD 가 이 값과 같다 (2026-09-07 확인)"
},
"ssotSha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"ssotSha256": "3c899a787af8468951023130b3ae9366e101ca17102a6c2da171e3fddfe901d3",
"sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916",
"generatedAt": "2026-09-11",
"generatedAt": "2026-09-18",
"candidateScope": {
"document": "final/document.md",
"sections": [
@@ -67,8 +67,8 @@
"counts": {
"topics": 16,
"nodes": 123,
"written": 112,
"unwritten": 11,
"written": 123,
"unwritten": 0,
"unlisted": 0,
"candidates": 1088
},
@@ -166,7 +166,13 @@
"concept:transaction-result-algebra"
],
"kind": "concept",
"publication": "미작성"
"publication": "초안",
"file": "commit-ambiguity-as-a-result/concept/concept-publish-evidence-and-completion.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
],
"reference": [
@@ -272,7 +278,13 @@
"reference:unknown-is-a-third-result"
],
"kind": "decision",
"publication": "미작성"
"publication": "초안",
"file": "commit-ambiguity-as-a-result/decision/decision-record-the-evidence-first-choose-the-conclusion-later.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
]
}
@@ -406,7 +418,7 @@
]
},
{
"title": "시작 검증기 13개 규칙이 유일한 조립 지점에서 호출지 않는다",
"title": "확인한 자동설정 경로가 시작 검증기 13개 규칙을 호출지 않는다",
"kind": "case",
"slug": "thirteen-startup-rules-never-run",
"readiness": "READY",
@@ -421,7 +433,7 @@
"evidence": [
"`evidence/raw/267-grpc-family-reachability.txt`"
],
"classification": "validator가 5개 그룹 13개 규칙을 갖고(transport·security 4 / executor 2 / methods 4 / channels 2 / advanced isolation 1) javadoc이 그 13개를 고른 기준을 \"None of them fails a smoke test\"로 적는다. 유일한 조립 지점인 자동설정은 `@Bean` 9개를 만들면서 이 validator를 부르지 않고, static 메서드라 빈이 될 수도 없다. CLAUDE.md가 인용한 \"streaming method가 Stable catalog에 등록되면 startup을 거부한다\"와 §2.2의 runtime 강제가 둘 다 이 validator를 통해서만 성립하므로 둘 다 실행되지 않는다.",
"classification": "gRPC 시작 검증기는 13개 규칙을 갖지만, searched direct caller와 확인한 auto-configuration 경로에서는 실행 연결을 찾지 못했다. direct reference 0만으로 runtime 전체 미실행을 확정하지 않고 lifecycle·framework discovery와 실제 boot evidence를 별도로 확인해야 한다.",
"missing-verification": "build-only 가족이라 부팅 확인이 불가능하다 — 채택 시점에만 관측 가능",
"relations": [
"`reference:the-startup-validator-follows-the-autoconfiguration-root`",
@@ -542,7 +554,13 @@
"case:scan-exclusion-without-an-owner"
],
"kind": "case",
"publication": "미작성"
"publication": "초안",
"file": "assembly-ownership/case/case-the-guard-is-on-and-the-service-is-not.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
],
"concept": [
@@ -651,7 +669,7 @@
"evidenceFiles": []
},
{
"title": "시작 검증기가 도는지는 그 능력에 자동설정 루트가 있는지와 일치한다",
"title": "시작 검증기는 실제 lifecycle과 assembly 경로에서 실행 여부를 확인한다",
"kind": "reference",
"slug": "the-startup-validator-follows-the-autoconfiguration-root",
"readiness": "READY",
@@ -659,12 +677,12 @@
"`final/document.md#5-3`",
"`final/document.md#a18` (SRC-158)"
],
"classification": "루트가 있으면 검증기를 매달 자리가 있고, 없으면 검증기는 컴포넌트 스캔이 닿기를 기대하는데 그 스캔이 그 패키지를 제외하고 있을 수 있다. 기준은 \"이 검증기를 부르는 조립 지점이 어디인가\"를 능력 단위로 묻는 것이다.",
"classification": "시작 검증기의 실행 여부는 direct caller 하나로 확정하지 않는다. @Bean·component scan·auto-configuration, lifecycle callback, application event·post processor, framework discovery와 실제 boot evidence를 따라가며 누가 검증기를 실행하는지 확인한다. 확인한 auto-configuration이 직접 호출하지 않는다는 사실과 runtime 전체 미실행 주장은 구분한다.",
"scope": [
"자동설정과 컴포넌트 스캔을 함께 쓰는 조합. 확인 결과: app-bootstrap 12종 배선(고아 0), messaging 6종 배선, web·websocket 미배선."
"자동설정과 컴포넌트 스캔을 함께 쓰는 조합. direct reference 0은 출발점이고 lifecycle/assembly 경로를 함께 닫아야 runtime 미실행을 말할 수 있다."
],
"exceptions": [
"**루트가 있는데도 부르지 않는 경우**가 둘 있다(`WebPlatformStartupValidator`, `GrpcPlatformStartupValidator`). 필요조건이지 충분조건이 아니다."
"자동설정 루트가 있어도 검증기를 호출하지 않을 수 있고, 반대로 다른 lifecycle 경로가 실행할 수도 있다. auto-configuration root 존재는 필요충분조건이 아니다."
],
"relations": [
"`case:thirteen-startup-rules-never-run`",
@@ -894,7 +912,13 @@
"reference:off-must-be-structural"
],
"kind": "decision",
"publication": "미작성"
"publication": "초안",
"file": "assembly-ownership/decision/decision-destructive-admin-operations-are-not-autoconfigured.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
]
}
@@ -1769,7 +1793,13 @@
"reference:register-paths-bind-values"
],
"kind": "decision",
"publication": "미작성"
"publication": "초안",
"file": "bounding-by-type/decision/decision-a-keyset-page-has-no-offset-field.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
},
{
"title": "JSONB 문서 안에 타입 메타데이터를 넣지 않는다",
@@ -1789,7 +1819,13 @@
"case:mongo-default-throws-on-first-write"
],
"kind": "decision",
"publication": "미작성"
"publication": "초안",
"file": "bounding-by-type/decision/decision-no-type-metadata-inside-a-jsonb-document.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
]
}
@@ -1916,7 +1952,7 @@
]
},
{
"title": "forwarded 헤더 신뢰 판정이 Nginx에만 있고 Java 정책 421 LOC은 배선되지 않았다",
"title": "테스트 Nginx 설정은 forwarded 헤더를 교체하지만 운영 경로는 확인하지 않았다",
"kind": "case",
"slug": "trust-policy-lives-in-nginx-not-in-the-code",
"readiness": "READY",
@@ -1931,8 +1967,8 @@
"evidence": [
"없음 — 도달성 확인과 설정 파일 대조"
],
"classification": "forwarded 헤더를 어디까지 믿을지 판정하는 Java 정책이 421 LOC 작성돼 있고 배선되지 않는다. 실제 판정은 Nginx 설정이 한다. 두 곳이 어긋나면 코드 리뷰가 잡을 수 없고, Java 쪽을 고쳐도 동작이 바뀌지 않다.",
"missing-verification": "Nginx 설정이 실제로 어떤 hop을 신뢰하는지 런타임에서 확인하지 않았다",
"classification": "확인한 nginxProxyTest 설정은 들어온 forwarded 헤더를 authoritative value로 교체한다. Java 쪽 정책은 searched direct reference 기준으로 사용 지점이 매우 적다. 다만 이 테스트 설정이 실제 운영 배포의 trust boundary인지와 Java 정책의 모든 framework/lifecycle wiring 부재까지는 이번 evidence로 확인하지 않다.",
"missing-verification": "테스트 Nginx 설정이 실제 운영 배포에도 사용되는지, Java 정책에 정적 직접 참조 밖의 lifecycle/framework wiring이 있는지 확인하지 않았다",
"relations": [
"`reference:check-which-duplicate-is-wired`",
"`reference:a-bean-is-not-composition-evidence`"
@@ -2013,7 +2049,13 @@
"reference:check-which-duplicate-is-wired"
],
"kind": "case",
"publication": "미작성"
"publication": "초안",
"file": "duplicate-mechanisms/case/case-a-boundary-that-leaks-only-under-load.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
],
"concept": [],
@@ -2723,7 +2765,13 @@
"reference:read-the-clock-after-the-lock"
],
"kind": "case",
"publication": "미작성"
"publication": "초안",
"file": "owner-safe-state-machines/case/case-a-mark-that-meant-seen-not-projected.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
],
"concept": [
@@ -3792,7 +3840,13 @@
"concept:transport-failure-stage-and-category"
],
"kind": "concept",
"publication": "미작성"
"publication": "초안",
"file": "http-failure-classification/concept/concept-three-failure-vocabularies.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
],
"reference": [
@@ -3813,7 +3867,13 @@
"decision:an-unrecognised-sqlstate-is-not-guessed"
],
"kind": "reference",
"publication": "미작성"
"publication": "초안",
"file": "http-failure-classification/reference/reference-one-sqlstate-with-two-contributors-fails-startup.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
],
"question": [],
@@ -3871,7 +3931,13 @@
"reference:unknown-is-a-third-result"
],
"kind": "decision",
"publication": "미작성"
"publication": "초안",
"file": "http-failure-classification/decision/decision-an-unrecognised-sqlstate-is-not-guessed.md",
"status": "게시 전",
"studioId": "",
"assets": [],
"assetFiles": [],
"evidenceFiles": []
}
]
}
@@ -4351,7 +4417,7 @@
"kinds": {
"case": [
{
"title": "RLS가 아무것도 하지 않는 세 가지 방법",
"title": "RLS 검증에서 구분해야 할 우회와 미적용 경로",
"kind": "case",
"slug": "three-ways-rls-does-nothing",
"readiness": "READY",
@@ -4364,15 +4430,16 @@
"`db/experimental-rls/V1__tenant_rls.sql`"
],
"evidence": [
"없음 — verifier가 검사하는 세 조건과 마이그레이션 주석"
"없음 — verifier가 읽는 catalog 값과 migration 주석을 PostgreSQL RLS 의미에 맞춰 재해석"
],
"classification": "`RlsPolicyVerifier`가 RLS가 조용히 무력화되는 세 경로를 전부 확인한다 — policy 없음/RLS 미활성 · 런타임 롤이 `BYPASSRLS` 보유 · **런타임 롤이 테이블을 소유**(FORCE 없으면 면제). 세 번째가 가장 놓치기 쉽다. 그리고 unique index 하나에도 같은 축의 주석이 있다 — tenant 격리가 컬럼에 의존하면 그 테이블의 **모든 uniqueness 요구**에 그 컬럼이 들어가야 하고, `(value)`만의 unique index는 다른 tenant가 그 값을 썼다는 이유로 insert를 실패시켜 **버그이자 정보 유출**이 된다. 다만 `final/document.md#a05` §97이 기록하듯 verifier 자신이 \"반드시 보호돼야 하는 table\"의 부재를 성공으로 인정하는 결함을 갖는다.",
"classification": "RLS 활성 여부, runtime role의 superuser/BYPASSRLS, table owner와 FORCE ROW LEVEL SECURITY, applicable policy 유무는 서로 다른 분기다. RLS가 활성화된 일반 role에 applicable policy가 없으면 default deny가 적용된다. owner는 기본 우회하지만 FORCE RLS로 policy 대상이 될 수 있다. tenant 내부 유일성과 global 유일성도 구분한다. verifier가 required table 부재를 성공으로 인정하는 별도 coverage 결함은 유지된다.",
"missing-verification": "세 조건을 하나씩 깨서 verifier가 실제로 잡는지 확인하지 않았다",
"relations": [
"`concept:rls-three-preconditions`",
"`reference:hibernate-filter-is-not-a-security-boundary`",
"`reference:tenant-column-belongs-in-every-unique-constraint`"
],
"missing_verification": "실제 PostgreSQL 환경에서 RLS off, BYPASSRLS, owner/FORCE, policy 없음 분기를 각각 실행 재현하지 않았다",
"publication": "초안",
"file": "multitenancy-isolation/case/case-three-ways-rls-does-nothing.md",
"status": "게시 전",
@@ -4390,7 +4457,7 @@
],
"concept": [
{
"title": "RLS가 성립하기 위한 세 전제",
"title": "PostgreSQL RLS 적용 여부를 가르는 분기",
"kind": "concept",
"slug": "rls-three-preconditions",
"readiness": "READY",
@@ -4403,8 +4470,8 @@
"`.../experimental/multitenancy/RlsPolicyVerifier.java`",
"`.../RlsTenantSessionBinder.java`"
],
"classification": "PostgreSQL RLS가 실제로 격리하려면 세 가지가 동시에 참이어야 한다는 설명이다. (1) `ENABLE ROW LEVEL SECURITY` — 테이블에 policy를 켠다. (2) `FORCE ROW LEVEL SECURITY` — **테이블 OWNER에게도** 적용한다. 없으면 owner는 자기 policy에서 면제되고 **owner는 흔히 마이그레이션 롤이며 그것이 사람들이 테스트하는 롤이다.** (3) 런타임 롤이 `BYPASSRLS`를 갖지 않는다 — 이것은 테이블 속성이 아니라 롤 속성이라 startup에서 assert해야 한다. policy가 `current_setting('app.tenant_id', true)`를 쓰는 이유(두 번째 인자 `true`가 미설정 시 raise 대신 NULL을 반환하고, NULL은 절대 `tenant_id`와 같지 않으므로 fail-closed)도 함께 다룬다.",
"missing-verification": "실제 RLS 환경을 세워 세 전제를 하나씩 깨보지 않았다",
"classification": "PostgreSQL RLS를 세 개의 동시 전제로 설명하지 않는다. RLS가 꺼져 있으면 policy가 적용되지 않고, superuser/BYPASSRLS는 우회한다. table owner는 기본 우회하지만 FORCE ROW LEVEL SECURITY가 켜지면 policy 대상이 된다. policy 대상 role에 applicable policy가 없으면 default deny이고, policy가 있으면 USING/WITH CHECK를 평가한다. current_setting('app.tenant_id', true)의 fail-closed 성질은 이 분기와 별도로 유지한다.",
"missing-verification": "실제 PostgreSQL 환경에서 각 분기를 하나씩 재현하지 않았다",
"relations": [
"`case:three-ways-rls-does-nothing`",
"`reference:hibernate-filter-is-not-a-security-boundary`",