From 37d3a1e152707d2da587440c4cea8601b9ef0fd2 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Sun, 27 Sep 2026 13:14:38 +0100 Subject: Lay Pure out as a core with the parts selected around it The app model, the tree store and the HTTP host lived in prototypes/connections among the earlier document runtime, the store was known only by its format's name, and Vega compiled six Pure sources by path. Now, as the README's principle has it: core/ runtime, flow and the app model (module*.c), with what the core asks of storage (state.h, store.h) store/ the tree store (PURETRE3, PUREREC1) serve/ pure-serve: HTTP host, outbound delivery, JSON in place cli/ pure-run, pure-edit-module A top-level make builds libpure.a (app model and store, for hosts to link) and the hosts; make check runs the store's and the app model's tests. The earlier runtime still builds from prototypes/connections against core/ and store/ (check, check-tree and check-core pass). Design 009 records the rule: parts by role behind libraries; git repositories split by coupling, so the store stays here while its contract moves with the core, and Vega stays apart. --- docs/design/008-module-format.md | 4 +- docs/design/009-layout.md | 60 ++++++++++++++++++++++++ docs/research/018-native-connections.md | 2 +- docs/research/019-byte-storage.md | 2 +- docs/research/030-authenticated-document-peer.md | 2 +- docs/research/064-release-callback-reentrancy.md | 2 +- docs/research/066-application-boundary.md | 2 +- docs/research/073-single-file-persistent-tree.md | 2 +- docs/research/074-runtime-owned-drafts.md | 4 +- docs/research/075-http-host-against-axum.md | 2 +- docs/research/076-one-sync-commit.md | 2 +- 11 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 docs/design/009-layout.md (limited to 'docs') diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md index 3fd4d52..84aa97b 100644 --- a/docs/design/008-module-format.md +++ b/docs/design/008-module-format.md @@ -1,7 +1,7 @@ # Pure 모듈 형식 v5 -2026-09-25. 구현 `prototypes/connections/module.{h,c}`, `module-text.c`, `module-list.c`, 상태 저장 `module-save.c`, -트리 저장소 연결 `module-store.c`. +2026-09-25. 구현 `core/module.{h,c}`, `module-text.c`, `module-list.c`, 상태 저장 `module-save.c`, +모듈 파일 읽기 `module-store.c`, 트리 저장소 `store/store-tree.c`, HTTP 호스트 `serve/`. Pure가 실행하는 앱 정의의 형식이다. JVM이 class 파일 형식을 소유하듯 Pure가 이 형식과 검증 규칙을 소유한다. 생산자는 [aup](/srv/git/aup.git) 같은 툴체인이다. Pure는 소스·문법을 모르며, diff --git a/docs/design/009-layout.md b/docs/design/009-layout.md new file mode 100644 index 0000000..8cb9a78 --- /dev/null +++ b/docs/design/009-layout.md @@ -0,0 +1,60 @@ +# 009 — 배치: 코어와 고르는 부품 + +2026-09-27. 저장소 경계와 범위가 일관되지 않다는 지적에서 정한 것. + +## 문제 + +- README의 원칙은 "항상 있는 코어는 실행, 소유권, 자원 한도, 완료 규칙만 다룬다. 프로토콜, 저장소 구현, UI, 코덱은 앱에 + 맞게 고른다"였다. 그런데 소스는 원칙과 다르게 놓여 있었다. +- 지금 세대의 코어(모듈 검증·실행 `module*.c`), 트리 저장소(`store-tree.c`), HTTP 호스트(`serve.c`, `deliver.c`, + `message-json.c`)가 모두 `prototypes/connections/` 한 디렉터리에 이전 세대(000–077의 문서 런타임: `program.c`, + `store.c`, `server.c`, 편집기들)와 섞여 있었다. 이름만 보면 저장소는 형식 매직(`PURETRE2`)으로만 드러났고, 코어가 + 어디서 끝나는지 알 수 없었다. +- 소비자는 소스 파일 목록으로 연결했다. Vega는 Pure의 `.c` 여섯 개를 경로로 나열해 컴파일했다. Pure에 파일이 하나 + 늘면 Vega가 깨진다. +- 반면 사람을 위한 인터페이스(Vega)는 따로 된 저장소였다. 저장소와 네트워크는 "호스트 안"처럼 보이고 UI만 부품처럼 + 보였다. + +## 규칙 + +1. **역할로 나누고 역할로 부른다.** 코어, 저장소, HTTP 호스트, 명령행 호스트. 형식 이름(`PURETRE3`, `PUREREC1`)은 + 형식의 이름일 뿐 부품의 이름이 아니다. +2. **부품은 라이브러리와 헤더로 쓴다.** 소비자는 파일 목록이 아니라 `libpure.a`와 `core/`의 헤더로 연결한다. +3. **저장소(git) 경계는 결합도로 정한다.** 함께 바뀌는 것은 한 저장소에 두고 디렉터리로 나누며, 따로 바뀌는 것은 저장소를 + 나눈다. 부품이 된 디렉터리는 나중에 `git subtree split`으로 그대로 떼어 낼 수 있다. + +## 배치 + +| 디렉터리 | 역할 | 만드는 것 | +|---|---|---| +| `core/` | 실행·소유권·한도·완료(`runtime.c`, `flow.c`), 앱 모델(`module*.c`: 실행 전에 검증한 모듈, 트랜잭션인 이벤트, draft인 상태), 코어가 저장소에 요구하는 것(`state.h`, `store.h`) | `build/libpure.a`(저장소와 함께) | +| `store/` | 트리 저장소. 한 파일, 커밋은 한 연속 쓰기와 동기화 한 번 | `libpure.a` 안 | +| `serve/` | `pure-serve`: HTTP 호스트, 나가는 전달, 제자리 JSON | `build/pure-serve` | +| `cli/` | 명령행 호스트 | `build/pure-run`, `build/pure-edit-module` | +| `prototypes/` | 이전 문서 런타임. `core/`와 `store/`를 가져다 빌드한다 | `make -C prototypes/connections` | + +저장소 계약(`state.h`, `store.h`)은 코어 쪽에 둔다. 코어가 필요한 것을 정하고 저장소 구현이 그것을 채운다. 트리 +저장소는 그 계약의 한 구현이다(이전 세대의 `store.c`, `store-paged.c`도 `store.h`의 구현이다). + +## 왜 저장소를 따로 된 git 저장소로 떼지 않았나 + +트리 저장소를 바꾼 최근 커밋 12개 중 9개가 계약(`state.h`, `store.h`)을 바꿨고, 그중 5개는 같은 커밋에서 코어나 호스트도 +바꿨다. 스냅숏(`571824a`: 저장소 동결 + 모듈 리스트 공유 + 모듈 스냅숏), 대여(`1a3586d`: 저장소 익스텐트 + serve), 그룹 +커밋(`d4b01a7`), 샤딩(`694b60f`), 매핑을 나누는 핸들(`89d1777`: + 모듈 파일 읽기). 계약이 아직 코어와 함께 움직이고 +소비자가 Pure 하나뿐이다. 따로 된 저장소면 이런 +변경마다 두 저장소의 버전을 맞춰야 한다. 두 번째 소비자(예: 메신저의 메시지 저장)가 생기거나 계약이 멈추면 `store/`를 +그대로 떼어 낸다. + +## 왜 Vega는 따로이고 이름이 pure-ui가 아닌가 + +Vega는 두 가지다. aup 라이브러리(`use vega`: 목록·표·포커스·톤 같은 인터페이스 어휘)와 그 어휘를 사람에게 보이는 +표면들(TUI, CLI, NUI, 웹, 심볼과 GPU)이다. 어휘는 aup의 언어 기능과 함께 바뀌고, 표면은 Pure의 모듈 API(`libpure.a`) +위에 있다. Vega의 최근 커밋은 모두 Vega 안에서 끝났다. `pure-serve`가 Pure 안에 있는 것은 프로그램을 위한 표면(보기가 +GET, 입력이 POST)에는 어휘가 필요 없기 때문이다. 모듈 형식만으로 정해진다. 사람을 위한 표면은 어휘가 필요하다. JDK가 +`java.net`과 HTTP 서버를 담고 JavaFX를 따로 두는 것과 같은 선이다. `pure-ui`라는 이름은 UI가 런타임의 일부라는 뜻이 +되어, 런타임이 화면을 모른다는 분리와 어긋난다. + +## 확인 + +최상위 `make check`(저장소와 앱 모델의 검사), `prototypes/connections`의 `make check`·`check-tree`·`check-core`, aup +`make check`, Vega `make check`가 새 배치에서 통과한다. Vega는 이제 `libpure.a`를 링크한다. diff --git a/docs/research/018-native-connections.md b/docs/research/018-native-connections.md index f2c26bc..6747dd8 100644 --- a/docs/research/018-native-connections.md +++ b/docs/research/018-native-connections.md @@ -66,7 +66,7 @@ DB 호출은 아직 동기식이므로 디스크 작업 중 다른 연결 처리 동적 링크한 SQLite 자체를 sanitizer로 다시 빌드한 것은 아니다. [원자료·재현](../../prototypes/connections/results/2026-09-23-native-connections/README.md), -[전송 수명](../../prototypes/connections/flow.c), +[전송 수명](../../core/flow.c), [DB 연결](../../prototypes/connections/store.c), [호스트](../../prototypes/connections/server.c), [기존 CLI 연결](../../prototypes/workbench/remote.mjs). diff --git a/docs/research/019-byte-storage.md b/docs/research/019-byte-storage.md index faefecf..b58adcd 100644 --- a/docs/research/019-byte-storage.md +++ b/docs/research/019-byte-storage.md @@ -72,7 +72,7 @@ Linux AArch64에서 일반 빌드와 ASan·UBSan 빌드가 모두 통과했다. [원자료와 재현](../../prototypes/connections/results/2026-09-23-byte-storage/README.md), [직접 저장 구현](../../prototypes/connections/store.c), -[구간·수명 API](../../prototypes/connections/store.h). +[구간·수명 API](../../core/store.h). 018의 원자료는 당시 SQLite 구현의 기록으로 별도 보존한다. ## 다음 결정 diff --git a/docs/research/030-authenticated-document-peer.md b/docs/research/030-authenticated-document-peer.md index 1ff569d..6e1fa23 100644 --- a/docs/research/030-authenticated-document-peer.md +++ b/docs/research/030-authenticated-document-peer.md @@ -9,7 +9,7 @@ `pure-document-peer`는 TLS 1.3/TCP의 상호 인증, 인증서 SAN 이름 확인, `pure-doc/1` ALPN 뒤에 자체 이진 메시지를 사용한다. 이미 구현한 scalar 피어의 소켓·TLS 부분을 [`tls-link.c`](../../prototypes/common/tls-link.c)로 분리해 두 피어가 재사용한다. -코어의 [`runtime.c`](../../prototypes/connections/runtime.c), 저장소, `editor.pure`는 +코어의 [`runtime.c`](../../core/runtime.c), 저장소, `editor.pure`는 문서 CLI와 같다. 전송 코드는 선택 빌드의 호스트에만 있다. 서버가 보내는 40 B hello는 `PDOC`, 버전 1, 예약 3 B, **원본 .pure 소스** SHA-256이다. diff --git a/docs/research/064-release-callback-reentrancy.md b/docs/research/064-release-callback-reentrancy.md index 68ce819..f35f6ec 100644 --- a/docs/research/064-release-callback-reentrancy.md +++ b/docs/research/064-release-callback-reentrancy.md @@ -5,7 +5,7 @@ `running`이 꺼져 있었다. 콜백이 같은 영역의 티켓을 해제하거나 새 작업을 받아들이면, 바깥쪽 회수 코드가 작업 슬롯과 예산 장부를 계속 수정할 수 있었다. -[`runtime.c`](../../prototypes/connections/runtime.c)는 자원 해제 콜백 동안 해당 +[`runtime.c`](../../core/runtime.c)는 자원 해제 콜백 동안 해당 영역을 실행 중으로 표시하고 이전 상태를 복원한다. 새 상태 필드나 작업별 할당은 없다. [`test-runtime.c`](../../prototypes/connections/test-runtime.c)는 취소, 대기 작업 해제, 유효하지 않은 호스트 할당 반환에서 콜백이 같은 티켓을 diff --git a/docs/research/066-application-boundary.md b/docs/research/066-application-boundary.md index 44e6d86..bb96bed 100644 --- a/docs/research/066-application-boundary.md +++ b/docs/research/066-application-boundary.md @@ -5,7 +5,7 @@ ## 현재 코드가 증명하는 것과 못 하는 것 -`prototypes/connections/runtime.c`는 입력 접수, 바이트 예산, 작업 슬롯, 완료, +`core/runtime.c`는 입력 접수, 바이트 예산, 작업 슬롯, 완료, 대여와 다른 영역으로의 무복사 전달을 소유한다. 하지만 실행 단계에서 `PureProgram`의 저장 전용 `read`/`write`/`splice`를 직접 해석하고 `PureHost.read`/`write`/`splice`를 호출한다. 따라서 현재 5,980 B 코어는 diff --git a/docs/research/073-single-file-persistent-tree.md b/docs/research/073-single-file-persistent-tree.md index 13884e3..4386e93 100644 --- a/docs/research/073-single-file-persistent-tree.md +++ b/docs/research/073-single-file-persistent-tree.md @@ -74,7 +74,7 @@ Ctrl+S부터 저장 완료 프레임까지 잰다. 1 MiB 문서, 저장소별 3 ## 검증 -[`test-tree-store.c`](../../prototypes/connections/test-tree-store.c)는 ASan·UBSan에서도 통과했다. +[`test-tree-store.c`](../../store/test-tree-store.c)는 ASan·UBSan에서도 통과했다. - 4개 키에 6,000회 무작위 편집을 평면 배열 오라클과 매번 비교했다(최대 333,510 B, 높이 2). 매 편집 뒤 저장된 모든 비루트 노드가 기준 이상으로 채워졌는지 확인했다. diff --git a/docs/research/074-runtime-owned-drafts.md b/docs/research/074-runtime-owned-drafts.md index 71ac8c9..42fd417 100644 --- a/docs/research/074-runtime-owned-drafts.md +++ b/docs/research/074-runtime-owned-drafts.md @@ -1,7 +1,7 @@ # 074 — 작업 중인 값을 런타임이 소유한다 2026-09-25. [073](073-single-file-persistent-tree.md)의 선택형 트리 저장소 위에 런타임 API -[`state.h`](../../prototypes/connections/state.h)를 추가했다. 기본 빌드와 기본 저장 형식은 바뀌지 않았다. +[`state.h`](../../core/state.h)를 추가했다. 기본 빌드와 기본 저장 형식은 바뀌지 않았다. Pure는 JVM과 같은 위치의 런타임이다. 앱 기능은 별도 저장소에서 만들고, 이 저장소의 CLI·TUI· HTTP 호스트는 런타임을 검사하는 호스트다. 따라서 이번 단계는 편집기 기능이 아니라 모든 앱이 쓰는 @@ -61,7 +61,7 @@ draft가 가진 옛 매핑에서 필요한 노드를 새 파일로 복사한다( ## 검증 -[`test-tree-store.c`](../../prototypes/connections/test-tree-store.c) 추가 검사는 ASan·UBSan을 포함해 통과했다. +[`test-tree-store.c`](../../store/test-tree-store.c) 추가 검사는 ASan·UBSan을 포함해 통과했다. - 20,000회 무작위 편집을 draft에서 평면 오라클과 비교하고 1,000회마다 커밋했다(최종 419,324 B, 커밋 20회). 커밋 뒤에는 저장본 일치, revision 증가, 빈 arena를 확인했다. diff --git a/docs/research/075-http-host-against-axum.md b/docs/research/075-http-host-against-axum.md index 17cfe39..77be305 100644 --- a/docs/research/075-http-host-against-axum.md +++ b/docs/research/075-http-host-against-axum.md @@ -55,7 +55,7 @@ PUT은 연결마다 자기 키 앞에 16 B를 넣고 받은 ETag로 다음 `If-M ## 1단계 결과: `pure-serve` (소유 스레드 하나) -새 호스트 [`serve.c`](../../prototypes/connections/serve.c): 지속 연결·파이프라이닝, edge-triggered epoll, 연결 +새 호스트 [`serve.c`](../../serve/serve.c): 지속 연결·파이프라이닝, edge-triggered epoll, 연결 최대 1,024(받을 때 할당), `writev` 응답, 같은 런타임 코어와 묶음 커밋. 같은 조건에서 다시 쟀다. `strace -c`로 보니 GET 하나마다 저장소 draft를 여닫느라 시스템 호출이 9번(파일·디렉터리 열기, `fstat` 두 번, diff --git a/docs/research/076-one-sync-commit.md b/docs/research/076-one-sync-commit.md index 9f010e7..4b9afa6 100644 --- a/docs/research/076-one-sync-commit.md +++ b/docs/research/076-one-sync-commit.md @@ -94,7 +94,7 @@ p99는 장치 쪽 잡음이 지배한다. 꼬리를 늘리는 단위를 64 KiB, ## 검증 -[`test-tree-store.c`](../../prototypes/connections/test-tree-store.c)는 ASan·UBSan에서도 통과했다. 새로 넣은 검사: +[`test-tree-store.c`](../../store/test-tree-store.c)는 ASan·UBSan에서도 통과했다. 새로 넣은 검사: - **잃어버린 커밋**: 최신 커밋의 바이트 하나를 바꾸고 프로세스의 기억을 지우면(재부팅 뒤의 새 프로세스와 같다) 읽기, 핸들, writer가 모두 이전 커밋을 쓴다. 다음 커밋은 잃어버린 커밋이 있던 자리부터 쓴다. -- cgit v1.2.3