ADR 색인 — Architecture Decision Records
이 문서가 답하는 질문
- 이 저장소가 내린 구조적 결정에는 어떤 것들이 있고, 각각 어디에 기록되어 있는가?
- 어떤 결정이 아직 유효하고, 어떤 것이 다른 결정으로 대체되었는가?
- 특정 코드를 바꾸려 할 때 어떤 결정을 먼저 읽어야 하는가?
이 디렉터리의 성격
여기에 있는 ADR은 새로 만든 결정이 아니다. 이 저장소는 이미 결정을 내렸고, 그 근거가 README.md, .specify/memory/constitution.md, specs/00N/plan.md의 Complexity Tracking, 그리고 소스 주석에 흩어져 있었다. 각 ADR은 그것을 발굴해 정식화한 것이며, 모든 주장에 파일:라인 또는 스펙 번호 인용이 붙어 있다.
근거를 찾지 못한 결정은 여기에 없다. 확인되지 않은 것은 "미확인 / 미결정"으로 적는 것이 이 저장소의 규칙이다.
사실 (Facts)
| 항목 | 값 |
|---|---|
| 거버넌스 문서 | .specify/memory/constitution.md (v1.0.0, 비준 2026-08-05) |
| 헌법 원칙 수 | 10개 (I~X). NON-NEGOTIABLE: I(포크 금지), VIII(에이전트 네이티브), IX(ACID) |
| 헌법 이탈 기록 위치 | 각 specs/NNN-*/plan.md의 Complexity Tracking 절 |
| 스펙 수 | 14개 (specs/001-* ~ specs/014-*) |
| 커밋 이력 | 6개 커밋, 2026-08-15 ~ 2026-08-17 |
ADR 색인
| # | 제목 | 상태 | 날짜 | 영향 범위 |
|---|---|---|---|---|
| 001 | PostgreSQL 포크가 아닌 확장으로 구현한다 | Accepted | 2026-08-05 | 전 계층 |
| 002 | v1에서 Table Access Method를 구현하지 않는다 | Accepted (v2 재평가) | 2026-08-06 | storage |
| 003 | 식별자를 [shard:9][type:18][local:36] int8 비트필드로 인코딩한다 |
Accepted | 2026-08-06 | storage, cypher, typeql, cluster |
| 004 | 인접 정보를 CSR형 세그먼트(힙 튜플당 이웃 ≤256개)로 저장한다 | Accepted | 2026-08-06 | storage, cypher, security, ops |
| 005 | 프로퍼티를 타입 카탈로그가 생성한 실제 컬럼으로 저장한다 (미선언은 __ext) |
Accepted | 2026-08-06 | storage, catalog, cypher, vector, security |
| 006 | 미선언 프로퍼티를 쓰기 시점에 승격하고, 충돌 시 text로 단방향 확장한다 |
Accepted | 2026-08-17 | storage, catalog, cypher, vector |
| 007 | 상속 판정을 nested-set 구간 라벨의 단일 범위 비교로 수행한다 | Accepted | 2026-08-06 | catalog, cypher, typeql, adapters |
| 008 | Cypher를 최상위 문장 문법이 아니라 함수 호출로 진입시킨다 | Accepted (원칙 II 부분 미달) | 2026-08-06 | cypher, api, bolt, interop |
| 009 | 읽기 경로는 SQL 생성으로, 쓰기 경로는 Rust SPI로 이원화한다 | Accepted | 2026-08-06 | storage, cypher, typeql |
| 010 | access.sql의 접근 경로를 전부 LANGUAGE sql로 작성한다 |
Accepted | 2026-08-06 | storage, cypher, performance |
| 011 | 사용자 값을 SQL 텍스트로 보간하지 않고 단일 jsonb 파라미터로 바인딩한다 | Accepted | 2026-08-06 | cypher, security, storage |
| 012 | 가변 길이 경로를 트레일 열거에서 방문집합 BFS로 재작성한다 | Accepted | 2026-08-17 | cypher, storage, performance |
| 013 | 방문집합 BFS 재작성을 보수적으로만 적용한다 (관측 가능성 + 손익분기) | Accepted | 2026-08-17 | cypher, performance, correctness |
| 014 | 백엔드-로컬 CSR(og_csr_build)을 자동으로 적용하지 않는다 |
Accepted | 2026-08-17 | storage, cypher, security, ops |
| 015 | 하나의 그래프 위에 Cypher와 TypeQL 두 질의 언어를 제공한다 | Accepted (원칙 II 이탈 기록됨) | 2026-08-06 | typeql, cypher, catalog, api |
| 016 | TypeQL 관계를 간선이 아니라 노드로 물화한다 | Accepted | 2026-08-06 | typeql, storage, catalog, cypher |
| 017 | Bolt 게이트웨이를 배경 워커가 아니라 별도 프로세스로 둔다 | Accepted | 2026-08-06 | bolt, api, ops, architecture |
| 018 | Bolt 인증에 PostgreSQL role을 그대로 쓴다 (두 번째 사용자 저장소 없음) | Accepted | 2026-08-06 | bolt, security, api |
| 019 | 벡터는 pgvector를 재사용하고, 임베딩을 관계에도 부여한다 | Accepted | 2026-08-06 | vector, storage, catalog, cypher |
| 020 | 외부 네트워크 호출에 async 대신 blocking ureq를 쓴다 |
Accepted | 2026-08-17 | vector, compat, security, ops |
| 021 | 샤딩을 설계만 확정하고 구현하지 않는다 (읽기 복제만) | Accepted (원칙 VII 부분 미충족) | 2026-08-06 | cluster, storage, ops, api |
| 022 | TypeDB 공식 예제를 무수정으로 통과시키는 것을 적합성 기준으로 삼는다 | Accepted | 2026-08-06 | typeql, testing, docs |
| 023 | 벤치마크에 정확성 게이트를 두고, 불일치 시 성능 수치를 무효 처리한다 | Accepted | 2026-08-06 | testing, docs, performance |
| 024 | RDF 매핑 불가 구문을 og_triple_overflow에 원형 보존한다 |
Accepted | 2026-08-06 | adapters, data, catalog |
| 025 | 함수 권한은 기본 거부하고, 역할은 확장이 만들지 않는다 | Accepted | 2026-08-23 | security, api, data, operations |
| 026 | 세분화된 쓰기 권한은 PG 테이블/컬럼 GRANT + 레지스트리 RLS 로, 행위(Action)는 run_as 소유 SECURITY DEFINER 함수로 |
Accepted | 2026-09-30 | security, storage, api, bolt, audit |
| 027 | 이중 시간은 현재 테이블 + h_<type_id> 이력으로, 시점은 생성 뷰가 읽는 세션 설정으로. 답할 수 없는 질의는 오류 |
Accepted | 2026-09-30 | storage, cypher, typeql, bolt, catalog |
| 028 | 스키마 변경은 가능한 한 카탈로그(매핑) 수준에서, 표현 변경만 배치로 행을 다시 쓴다 (Expand/Migrate/Validate/Contract) | Accepted | 2026-09-30 | catalog, storage, cypher, security, temporal |
| 029 | 데이터 패브릭을 Ontological 안에 둔다 — 가상 타입은 FDW 위의 매핑 뷰, 가상 관계는 키 조인 | Proposed | 2026-10-09 | interop, cypher, catalog |
Superseded / Deprecated 항목은 현재 없다. 대체된 결정이 발생하면 상태 열을 Superseded by ADR-0NN으로 바꾸고, 대체 ADR에 근거를 남긴다.
헌법 원칙 ↔︎ ADR 대응표
| 원칙 | 요지 | 관련 ADR | 현재 상태 |
|---|---|---|---|
| I. 포크가 아닌 확장 (NON-NEGOTIABLE) | 표준 PG16에 CREATE EXTENSION |
001, 002, 008, 019, 021 | ✅ 충족 |
| II. Cypher 1급 | 문자열 함수 래퍼 거부 | 008, 010, 015 | ⚠️ 부분 — 진입점이 함수 호출 (ADR-008), 두 번째 언어 추가 (ADR-015) |
| III. 성능은 저장구조에서 | CSR형 인접, 실컬럼, 고정폭 ID | 002, 003, 004, 005 | ⚠️ 부분 — TableAM 미사용 (ADR-002) |
| IV. 온톨로지 우선 타이핑 | 상속 판정 상수 시간 | 007, 016 | ✅ 충족 |
| V. 벡터는 노드·엣지·경로 모두에 | pgvector 재사용, push-down | 019 | ⚠️ 노드·관계는 충족, 경로/서브그래프 임베딩은 미확인 |
| VI. 코어는 하나, 표준은 어댑터로 | 이중 저장 금지 | 015, 016, 017, 024 | ✅ 충족 |
| VII. 처음부터 확장 가능하게 | 복제 + 샤딩 | 003, 021 | ⚠️ 부분 — 샤딩 미구현 (ADR-021), Bolt ROUTE 형식만 |
| VIII. 에이전트 네이티브 (NON-NEGOTIABLE) | introspection, 교정 가능한 오류, 시간 축 | 022 (간접), 026, 027 | ⚠️ 부분 — introspection·오류는 spec 008 범위(별도 ADR 없음). 유효/기록 시각은 ADR-027 (가변 길이·TypeQL 시점 질의는 거부) |
| IX. ACID (NON-NEGOTIABLE) | 모든 그래프 변경이 MVCC/WAL | 002, 004, 009, 012, 014, 021 | ✅ 충족 (CSR은 자동 적용하지 않음으로써 유지) |
| X. 벤치마크로 증명 | 재현 가능한 측정 | 012, 013, 022, 023 | ✅ 충족 |
목적별 진입점 — 무엇을 바꾸기 전에 무엇을 읽어야 하는가
| 바꾸려는 것 | 먼저 읽을 ADR |
|---|---|
og_data.og_adj 스키마, 인접 갱신 로직 |
004, 002, 009 |
| 프로퍼티 저장·타입 추론·컬럼 승격 | 005, 006, 011 |
| Cypher 컴파일러의 가변 길이 경로 처리 | 012, 013, 010 |
og_reach / og_csr_* 순회 함수 |
012, 014, 010 |
| 타입 계층·상속 판정 | 007, 003 |
| Bolt 게이트웨이 | 017, 018, 008 |
| TypeQL 파서·컴파일러·저장 매핑 | 015, 016, 022 |
| 벡터 검색·임베딩 | 019, 005, 020 |
| 벤치마크 하네스·성능 주장 | 023, 012, 013 |
| 분산·복제·샤드 | 021, 003 |
| RDF/OWL 어댑터 | 024, 007 |
| 쓰기 권한·행위·스탬프·감사 | 026, 025, 005 |
| 이력·시점 질의, 생성 뷰의 temporal 분기 | 027, 012, 013 |
| 스키마 변경 파이프라인, 속성/타입 이름 매핑 | 028, 005, 007, 003 |
필수 / 금지 (Required / Forbidden)
이 목록은 위 ADR들에서 파생된 강제 규칙이다. 위반하려면 해당 ADR을 먼저 개정해야 한다.
금지 (Forbidden)
- PostgreSQL 커널 패치·포크를 전제하는 설계 — ADR-001, 헌법 원칙 I (NON-NEGOTIABLE).
- 프로퍼티를
agtype/JSONB 블롭 하나에 담는 구조 — ADR-005, 헌법 "금지 사항". - 상속 판정을 런타임 재귀 CTE로 하는 것 — ADR-007, 헌법 "금지 사항".
- 사용자 값을 SQL 텍스트에 보간하는 것 — ADR-011, spec 003 FR-026.
access.sql에LANGUAGE plpgsql/ C 집합 반환 함수를 추가하는 것 — ADR-010 (옵티마이저 장벽이 된다).og_csr_*경로를 Cypher 컴파일러가 자동으로 선택하게 만드는 것 — ADR-014 (MVCC·RLS를 조용히 포기하게 된다).- 벡터 검색 후 그래프 조건을 사후 필터링하는 것 — ADR-019, 헌법 원칙 V.
- 인접 구조를 비동기로 갱신하는 것 — 헌법 원칙 IX, ADR-009.
- 2PC와 장애 주입 검증 없이 분산 쓰기를 여는 것 — ADR-021.
- 시스템 간 결과 대조 없이 성능 수치를 발표하는 것 — ADR-023.
- 시점(
og.valid_at/og.system_at)이 설정된 질의에 현재 상태로 답하는 경로를 추가하는 것 — 레지스트리·인접 세그먼트만 읽는 새 질의 모양은og_require_current로 막아야 한다 — ADR-027. - 쓰기 권한을 엔진 쪽 검사로만 강제하는 것 — 판정은 PostgreSQL GRANT/RLS 가 한다 — ADR-026.
필수 (Required)
- 모든 그래프 구조는 일반 힙 릴레이션이어야 한다 (MVCC/WAL/
pg_dump상속) — ADR-001, 002. - 가변 길이 경로 재작성은 두 게이트를 모두 통과해야 한다: 다중도 관측 불가 + 손익분기 — ADR-013. 새 케이스는
engine/tests/sql/05_reachability.sql에 양방향으로 고정한다. - 인접 세그먼트를 읽는 컴파일 결과는 항상 대상 노드 뷰와 조인해야 한다 —
og_adj의 RLS는 쓰기 정책뿐이고 읽기는USING (true)이기 때문 (ADR-004, ADR-026, spec 005). - 헌법 원칙 이탈은 해당
plan.md의 Complexity Tracking에 (a) 위반 원칙, (b) 위반 없이 불가능한 이유, (c) 제거 계획을 기록해야 한다 — 헌법 Governance. 기록 없는 위반은 리뷰에서 반려된다. - 미지원 기능은 "부분은 부분"으로 명시한다 — spec 010 SC-010, ADR-022.
ADR 작성 규칙
새 ADR은 다음 형식을 따른다. 번호는 00_index.md의 마지막 번호 + 1.
# ADR-0NN: <결정 제목>
| 항목 | 값 |
|---|---|
| 상태 | Accepted / Superseded by ADR-0NN / Deprecated |
| 날짜 | YYYY-MM-DD (확인 가능한 경우만, 아니면 "미상") |
| 영향 범위 | storage, cypher, api ... |
| 근거 | README.md#..., engine/src/...:123, specs/00N/plan.md |
## 배경
## 고려한 선택지
## 결정
## 근거 ← 반드시 코드/문서 인용과 함께
## 결과 ← 긍정적 영향 / 부정적 영향·감수한 대가
## 재검토 조건 ← 형식적으로 채우지 말 것. 실제 트리거를 쓸 것
<!-- affects: ... -->
날짜 규칙: git log로 확인 가능한 것만 실제 날짜를 쓴다. 스펙 문서에 기록된 날짜는 그 문서의 기준일임을 함께 밝힌다. 확인 불가하면 "미상".
정직성 규칙: 문서는 완벽할 필요는 없지만 거짓이어서는 안 된다. 계획서와 구현이 어긋나 있으면(예: ADR-004의 CHUNK_SIZE) 그 사실을 적는다.