BIBLIOGRAPHY
History
- Neomacs 3주차: NixOS 빌드 실증(10분40초), 339K줄 Rust, 97/98 C파일 rust-default, loadup→pcase 도달. GPU 디스플레이는 실용 단계, Rust 코어는 GC/VM 미해결.
- Neomacs 하루 추적: 90커밋/17.5시간, 273K줄 Rust. 커버리지 체계 분석(1,434 primitive-subr 중 runtime 59.8%). 단일 에이전트 운영 구조 확인. 사람은 경계 판단만.
- Neomacs 재검토: “Remacs 반복” 판단 철회. Oracle-Plan-Gate YOLO 루프 발견. 4,158커밋/보름, 261K줄 Rust. 접근법이 근본적으로 다르다.
- 멀티 데몬 GC + native-comp 실증 벤치마크. 12.2분→7.2분(-41%), native-comp 미적용 버그 3개 발견/수정.
- 아직 리서치 중이다.
- 생성 Incremental modernization of the GNU Emacs C core with Zig. No GPU glitter. Looks the same. Never freezes.
References
emacs-ng/emacs-ng
(emacs-ng [2020] 2026) A new approach to Emacs - Including TypeScript, Threading, Async I/O, and WebRender.
2026-02-09 Epic review: codebase analysis and prior art study
Context
Epic 0 (bd-2ev) 시작 전, emacs-30.2 실제 코드 기준으로 타겟 파일 분석 + 선행 프로젝트(Neomacs, emacs-ng, Remacs) 조사를 수행했다.
Codebase analysis: actual line counts
AGENTS.md에 기록된 수치를 실측값으로 보정 필요:
| File | AGENTS.md | Actual | Test lines | Test cases |
|---|---|---|---|---|
| process.c | 9,090 | 9,038 | 1,023 | 37 |
| alloc.c | 7,604 | 8,358 | 62 | 5 |
| keyboard.c | 14,612 | 14,071 | 79 | 2 |
| eval.c | 4,647 | 4,553 | 374 | 20 |
| thread.c | 1,314 | 1,221 | 421 | 33 |
| systhread.c | 553 | 544 | — | — |
| systhread.h | 160 | 113 | — | — |
| sysdep.c | 4,717 | 4,763 | — | — |
| emacs.c | 3,645 | 3,759 | — | — |
| lisp.h | 5,914 | 5,939 | — | — |
Critical test gaps: alloc-tests.el (5건), keyboard-tests.el (2건).
Key technical findings
Existing infrastructure (more mature than expected)
thread_select()already releasesglobal_lockduringpselect()→ I/O thread separation can build on this pattern, not start from scratchmark_stackalready exists in alloc.c (lines 7104-7159) → incremental GC can slice via counter, no new data structure neededmaybe_quit()already ineval_sub()(line 2500),Ffuncall()(line 3076) → existing yield points are present, but gaps remain
Missing yield point in Fprogn
Fprogn() (eval.c:428-443) has NO quit check in its loop:
while (CONSP (body)) {
val = eval_sub (form); // eval_sub has maybe_quit inside
body = XCDR (body); // no check here
}eval_sub 내부에 maybe_quit() 가 있으므로 대부분 문제없지만, form이 C subr이고 매우 빠르게 끝나는 경우가 수천 번 반복되면 quit 체크 지연 발생.
Threading model: cooperative + global lock (GIL-like)
- One
global_lock(sys_mutex_t) guards all Elisp execution - Only 3 yield points:
thread_select,condition-wait,thread-yield block_input/unblock_inputused in 65 files — must preserve semanticsxdisp.cexclusion validated: no direct calls to/from target files
Prior art: Neomacs (https://github.com/eval-exec/neomacs)
- Rust. Full rewrite approach. Emacs 31 master fork.
- xdisp.c 50,000줄 → Rust 4,000줄 대체 (GPU rendering via wgpu)
- 1인 프로젝트. 6일차. 커서 이펙트 수십 종에 집중 중.
- 코어 재작성(eval, GC, bytecode)은 “planned” — 어려운 문제를 뒤로 미루는 패턴
- Remacs 실패 분석 문서(elisp-core-analysis.md)는 참고 가치 높음
- eval↔data↔buffer 순환 의존성 정량 분석
- “eval.c는 모듈이 아니라 결합 조직(connective tissue)”
- 판단: 과하다. 30만줄 C를 1명이 재작성하는 건 Remacs의 반복.
Prior art: emacs-ng (https://github.com/emacs-ng/emacs-ng)
- C + Rust + JS(Deno). Fork + additive layer. 2020년 시작.
- TypeScript + WebRender + Async I/O + WebWorker를 동시 추진
- 사실상 사망. 마지막 인간 커밋 2025-02. 유지보수자 본인이 “Nobody is working on this” (이슈 #637).
- 핵심 실패: 이벤트 루프 통합
- 같은 스레드 → “very fragile” (Lisp 타이머로 JS 루프 수동 펌핑)
- 별도 스레드 → “too ambitious” (PR #463, 2년째 WIP)
- Deno API 빠른 변화 → 업그레이드 불가 → JS 기능 전체 비활성화
- 원 창시자(DavidDeSimone) 2021년 이탈 후 1인 유지보수로 전락
- 교훈: 범위를 넓히면 죽는다. 이벤트 루프 통합이 진짜 어려운 문제.
Prior art: Remacs (dead since 2021)
- C→Rust 1:1 변환. 4,613 stars.
- 리프 함수(쉬운 것)부터 시작 → 코어(GC, bytecode)에서 멈춤
- 교훈: 언어를 바꾸는 것 자체가 목적이 되면 안 된다.
Lessons for I Am Emacs
- 범위를 좁혀라 — 모든 실패 프로젝트가 범위 과다
- 이벤트 루프 통합이 핵심 난관 — emacs-ng의 실패 핵심이자 우리 Epic 1의 핵심
- 새 언어 런타임 추가 금지 — emacs-ng Deno 악몽
- 화려한 것(GPU)은 쉽고, 기반(GC, 스레딩)은 어렵다
- Zig @cImport = bindgen 문제 회피 (emacs-ng 대비 이점)
- 기존 인프라(thread_select, mark_stack) 위에 쌓아야지 재작성하면 안 된다
org-agenda scenario analysis
org-agenda 100개 파일 로딩 시나리오를 분석한 결과, 병목 위치가 초기 가정과 다름:
- 파일 I/O: fileio.c → emacs_read() 경로. process.c/pselect() 아님. → process.c I/O 스레드 분리는 org-agenda 체감에 거의 영향 없음.
- Elisp 파싱: eval.c Feval/Fprogn 루프. 5-20초. 진짜 병목.
- GC: 임시 객체 대량 생성 → 수백ms × 여러 회. block_input 안에서 전체 멈춤.
process.c I/O 스레드가 도움 되는 시나리오는 별개: compile, LSP, TRAMP, shell — subprocess/network I/O가 메인스레드를 점유하는 경우.
Correction: “simple fixes” are misleading
초기에 “Fprogn yield = 1줄”, “GC slicing = ~50줄”로 과하게 단순화했으나 이는 오해:
- Fprogn maybe_quit(): 만약 이게 답이었으면 Emacs 개발자들이 이미 했을 것. 성능 오버헤드, 예외 처리 상호작용, specpdl 스택 상태 등 미검증 사항이 있음.
- GC slicing: incremental GC에는 write barrier 가 필수. mark 도중 Lisp_Object mutation이 발생하면 live object를 놓침. write barrier = Lisp_Object 변경 연산 전체에 훅을 거는 근본적 변경. Java, Go 등 모든 주요 런타임이 이를 위해 수년을 투자함.
- I/O thread: thread_select 패턴이 있지만, global_lock 재설계 없이는 진짜 비동기가 아니라 “I/O 대기 중 다른 cooperative 스레드 실행 가능” 수준.
결론: 이건 짜투리 작업이 아니다. 40년 된 아키텍처 위에서 하위 호환 100%를 유지하면서 세 가지 병목(I/O, GC, yield)을 동시에 해결하려면 설계부터 제대로 해야 한다. 이 프로젝트의 진짜 가치는 코드가 아니라 아키텍처 설계 문서 에 있다.
Decision: approach selection — PENDING
A/B/C 선택지는 보류. 먼저 해야 할 것:
- 각 병목의 진짜 난이도를 정직하게 평가 (write barrier, global_lock 등)
- Emacs 상류(upstream)에서 이 문제들이 왜 해결되지 않았는지 기존 논의 조사
- 확장 가능한 아키텍처 설계 — 이벤트 기반 코어의 구체적 설계 문서 작성
- 그 설계를 기반으로 Epic 구조 재편
다음 세션에서 아키텍처 설계에 집중할 것.
2026-02-09 Upstream research: GC, threading, responsiveness
emacs-devel 메일링리스트 및 커뮤니티에서 GC, threading, responsiveness 관련 논의를 전수 조사했다. 이 리서치는 Epic 구조 재편의 근거가 된다.
feature/igc (Incremental GC via MPS) — 게임 체인저
Emacs 공식 incremental GC가 feature/igc 브랜치에서 2년째 개발 중이며, 2026-01에 master 머지 절차가 시작되었다.
| 시점 | 내용 | 참여자 |
|---|---|---|
| 2024-02 | Gerd Moellmann이 MPS(Memory Pool System) 기반 IGC 제안 | Gerd Moellmann |
| 2024-07 | scratch/igc → feature/igc 승격 | |
| 2024-12 | 사용자 보고: pauses shorter and predictable | Oscar Fuentes, Pip Cet |
| 2025-02 | 벤치마크: GC-heavy 작업 2.7배 빠름, 메모리 3-6배 증가 | Helmut Eller |
| 2025-04 | Eli Zaretskii, 머지 전 5가지 차단 이슈 제시 | Eli Zaretskii |
| 2026-01 | Sean Whitton 머지 절차 시작, Helmut Eller 분리 패치 제출 | Sean Whitton, Helmut Eller |
핵심 기술: SIGSEGV 기반 page protection으로 write barrier 구현. OS 레벨 메모리 페이지 보호 → Lisp_Object mutation 시 시그널로 감지. C 코드 전체에 수동 write barrier 삽입 불필요.
벤치마크 (Helmut Eller, 2025-02):
- boehm-gc: IGC 6.79s vs Master 18.49s (2.7배 빠름)
- 메모리: IGC가 3-6배 더 사용
Eli Zaretskii의 5가지 차단 이슈:
- MPS가 일부 플랫폼 미지원 (ARM Linux, RPi 등)
- upstream MPS에 없는 패치 필요
- MPS 유지보수 불확실 (마지막 릴리스 2년 전)
- SIGSEGV를 정상 동작에 사용 (디버깅 복잡)
- igc.c FIXME 잔존, 테스트 커버리지 미비
→ Epic 2(Incremental GC) 재검토 필요. IGC가 머지되면 GC 문제는 공식 해결.
GC 이전 제안들 (IGC 이전)
- Daniel Colascione (2021): fully copying & compacting GC 제안 → 시간 부족으로 중단
- Daniel Colascione (2018): 재귀적 mark_object를 큐 기반으로 변환 제안
- Pip Cet (2017): SpiderMonkey GC 실험 → 이후 MPS/IGC로 방향 전환
- Opportunistic GC (2019/2021): idle 시 기회적 GC 실행
→ 모두 미완성 또는 중단. IGC가 유일한 생존 제안.
EmacsConf 2023: GC 통계 (Ihor Radchenko)
129명 사용자, 100만+ GC 기록 수집:
- 44%: GC pause 0.1초 미만 (인식 불가)
- 56%: 0.1초+ 눈에 띄는 지연
- 25%: 0.2초+ 심각한 지연
- 전체 GC 이벤트의 ~9%가 문제적 프리즈
- “agglomerated GCs”: 연속 100회+ GC → 누적 지연
Threading: 10년의 역사
| 시점 | 내용 | 결과 |
|---|---|---|
| 2009 | Tom Tromey + Scrivano, cooperative threading 구현 | Gnus 별도 스레드 |
| 2016-10 | Philipp Stephani, Go-style CSP 제안 (libtask) | “most things work” |
| 2016-12 | Eli Zaretskii, concurrency 브랜치 master 머지 | Emacs 26.1 탑재 |
| 2018 | Chris Wellons, ThreadSanitizer 검증 | ”many data races” |
| 2022 | Troy Hinckley, multi-threaded Emacs 비전 | CSP + copy-on-demand |
| 2023 | Ihor Radchenko, isolated process/thread 제안 | 192답글, 합의 없음 |
GIL 제거 시도: 없음. 합의된 대안도 없음.
GIL이 존재하는 이유:
- dynamic scoping, 버퍼=글로벌 객체, 수십 년의 Elisp 코드가 단일 스레드 가정
- Emacs 26 스레드: “many data races, completely untrustworthy” (Chris Wellons)
- Emacs 매뉴얼: “correct programs should not rely on cooperative threading”
C-g / maybe_quit() 메커니즘
maybe_quit()가 40개 C 파일, 111곳에 수동 삽입- bytecode.c:
quitcounter(unsigned char) → 255번째 역방향 branch마다 1회 체크 - C 내부 루프, 시스템 콜 대기, GC 중에는 작동 불가
- GUI에서 C-g는 메인 Lisp 스레드에서 처리 → 서브프로세스 대기 중 막힘
- TTY에서는 SIGINT → 즉각 중단 (GUI와 결정적 차이)
Rune (Troy Hinckley) — 가장 가까운 비전
Rust로 작성된 실험적 Emacs 코어. 우리 프로젝트와 구조적으로 유사:
- OS 스레드 사용 (green thread/async-await 거부)
- CSP 채널 기반 통신 (우리의 BlockingQueue + EmacsEvent와 유사)
- copy-on-demand 변수 바인딩 (함수=전역불변, 값=thread-local가변)
- 스레드별 독립 GC
- EmacsConf 2024 발표: https://emacsconf.org/2024/talks/rust/
- 블로그: https://coredumped.dev/2022/05/19/a-vision-of-a-multi-threaded-emacs/
비동기 Elisp 라이브러리 현황
| 라이브러리 | 접근 | 한계 |
|---|---|---|
| emacs-async | 별도 Emacs 프로세스 생성 | 마커/버퍼 전달 불가 |
| aio.el | generator 기반 async/await | 메인 스레드에서만 실행 |
| deferred.el | JS Deferred/promise 패턴 | 콜백 헬 |
| lsp-bridge | Python async RPC | 외부 프로세스 의존 |
| emacs-lsp-booster | Rust I/O 래퍼 | LSP 전용 |
→ 모든 해법이 “메인 스레드 밖으로 빼기” — Emacs 내부에서는 해결 불가.
I/O 스레드 분리: 아직 빈 자리
- GC → feature/igc가 해결 예정
- Threading/GIL → 아무도 못 풀었고 합의 없음
- pselect() 블로킹을 별도 스레드로 빼는 작업 → 아무도 하고 있지 않음
- 이것이 i-am-emacs 프로젝트의 독자적 가치가 될 수 있는 지점
비전 메모: i-am-emacs의 진짜 의미
Elisp 싱글스레드 로직은 유기체의 핏줄이다. 역류하지 않도록 한 줄로 간다. LLM이 이 루프에 방해되지 않는 구조에서 본딩된다면 이맥스의 재탄생이 가능하다.
homeagent-config(RPi5 + Zig + Go)도 같은 맥락: 에이전트를 디바이스에 입히려는 시도. i-am-emacs는 에이전트를 에디터에 입히는 시도. “나는 이맥스다. 무엇을 할 것인가? 사용자에게 무엇을 나눌 것인가?” — eval 루프에서 스스로 변모하는 유기적 협업 도구.
관련: gptel-agent (karthink), EAF (manateelazycat), lsp-bridge
소스 목록
GC
- MPS proposal (2024-02)
- IGC benchmarks (2025-02)
- Eli Zaretskii: 5 blocking issues (2025-04)
- Merging feature/igc (2026-01)
- EmacsConf 2023: GC stats (Ihor Radchenko)
- Emacs Horrors: Don’t Stop the World
Threading
- Tom Tromey: Emacs and Threading, Take 2 (2009)
- Concurrency, again (2016-10)
- Concurrency branch merged (2016-12)
- Chris Wellons: Emacs 26 Threads (2018)
- Troy Hinckley: Multi-threaded Emacs (2022)
Responsiveness
- C-g responsiveness (2025-08)
- JD Smith: async process output delivery points
- EmacsConf 2024: Rune (Troy Hinckley)
- Rune multi-threading issue #21
2026-02-10 멀티 데몬 Emacs: 프로세스 수준 동시성
단일 Emacs 내부의 어려운 문제(GC, GIL, I/O 스레드 분리)를 고치는 대신, OS 수준 프로세스 격리를 활용하면 어떨까? 같은 닷파일을 공유하는 여러 Emacs 데몬이 각각 전문 영역을 담당하는 구조. 이 아이디어는 denote-export 멀티 데몬 시스템 실전 경험에서 나왔다.
발단: denote-export 멀티 데몬 시스템
이미 동작하는 개념 증명이 doomemacs-config/bin/에 있다:
아키텍처:
- Python 오케스트레이터 (denote-export-parallel.py)
- N개 Emacs 데몬:
emacs --quick --daemon=denote-export-daemon-N --load denote-export.el - 라운드 로빈 방식 파일 분배
- IPC:
emacsclient -s 데몬이름 --eval '(함수 "파일")' - 모든 데몬이 동일한 Doom Emacs 설정 로드 (org, ox-hugo, citar, denote)
- 데몬별 자동 GC 튜닝 (gc-cons-threshold 16MB, 50파일마다 GC)
성능 결과:
- 데몬 4개: 1.8 files/sec (단일 배치 대비 18배 빠름)
- org 파일 2000개를 ~33분에 내보내기 완료
- 성공률 100%
- 메모리: 데몬당 ~200-300MB (4개 = ~1GB)
핵심 통찰: 프로세스 경계에서 어려운 문제가 사라진다. 각 데몬은 자체 GC(일시정지 격리), 자체 이벤트 루프(GIL 공유 없음), 자체 I/O(기본적으로 병렬)를 가진다. C 코드 수정이 필요 없다.
일반화: 전문 데몬 토폴로지
denote-export 패턴을 배치 처리 너머로 일반화할 수 있다:
┌─────────────┐
│ 조율자 │ (Python/Node)
│ (EPC/RPC) │
└──────┬───────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│org-daemon│ │git-daemon│ │lsp-daemon│
│ agenda │ │ magit │ │ eglot │
│ capture │ │ diff │ │ complete │
│ export │ │ blame │ │ diag │
└──────────┘ └──────────┘ └──────────┘
▲ ▲ ▲
└────────────┼────────────┘
┌──────┴───────┐
│ Main Emacs │
│ (UI + 편집) │
└──────────────┘각 데몬:
- 같은 닷파일 공유 (
~/.doom.d/등) - 조건부
(daemonp)체크로 도메인 패키지만 로드 - EPC로 조율자와 통신 (lsp-bridge/EAF 패턴)
- GC 격리, 크래시 격리, gc-cons-threshold 개별 튜닝 가능
메인 Emacs:
- UI 렌더링, 사용자 입력, 버퍼 편집 담당
- 무거운 작업을 전문 데몬에 위임
- 조율자 콜백을 통해 비동기로 결과 수신
선행 기술 조사
emacs-async (John Wiegley)
- 일회성
emacs --batch프로세스를 생성하여 계산 수행 - 콜백으로 결과를 메인 Emacs에 반환
- 한계: 영속 상태 없음, 버퍼/마커 공유 불가
- byte-compile, dired 복사 같은 단발 작업에 적합. 데몬 토폴로지에는 부적합.
emacs-mp (Nic Ferrier, 2014)
- 멀티 데몬 아키텍처에 가장 가까운 선행 기술
emacs --daemon=이름으로 영속 워커 생성- 인용: “Emacs가 fork도 못하고 진짜 스레드도 없으니, 별도 Emacs 데몬을 동시성 단위로 쓰자.”
- 한계: 상태 동기화 없음, 조율자 없음, 2017년 이후 방치
- 출처: https://github.com/nicferrier/emacs-mp
EAF (manateelazycat)
- Python-Qt 프로세스가 앱 호스트, EPC(RPC)로 Emacs와 통신
- 멀티스레드 Python이 렌더링, I/O 처리
- 경량 제어 메시지만 경계를 넘음
- 교훈: EPC 프로토콜이 Emacs ↔ 외부 프로세스 RPC에 잘 동작함
- 이건 외부 프로세스 위임이지, Emacs 간 위임은 아님
lsp-bridge (manateelazycat)
- Python async 프로세스가 LSP 클라이언트, EPC로 Emacs와 통신
- Python이 자동완성을 캐시하고 상위 N개만 Emacs에 전송
- 핵심 혁신: Emacs가 큰 LSP 페이로드를 안 봄 → GC 압박 없음
- 교훈: 조율자 패턴이 동작함. Python은 비동기를 자연스럽게 처리.
VS Code Extension Host
- 확장 실행용 별도 Node.js 프로세스
- UI 프로세스가 확장 작업으로 절대 멈추지 않음
- 메시지 포트를 통한 양방향 비동기 통신
- 교훈: 프로세스 격리 = 크래시 저항성 + 병렬성
Neovim 원격 플러그인
- Neovim 코어와 플러그인 호스트(언어 무관) 사이 msgpack-RPC
- 양방향 비동기: 플러그인이 Neovim API 호출, 이벤트 수신
- 교훈: 언어에 구애받지 않는 RPC 프로토콜이 생태계를 가능하게 함
실전 경험: denote-export에서 효과가 있었던 것
-
같은 닷파일, 다른 컨텍스트: 모든 데몬이 동일 설정을 로드. 설정 중복 없음.
(daemonp)로 런타임에 구분. -
Python이 오케스트레이터: ProcessPoolExecutor + emacsclient IPC. Python이 병렬 처리를 자연스럽게 담당; Emacs는 Elisp 평가만.
-
우아한 생명주기: 데몬 시작 → 준비 플래그 대기 → 작업 분배 → 결과 수집 → 데몬 종료. 시그널 핸들러로 정리.
-
데몬별 GC: 각 데몬의 GC는 해당 데몬만 일시정지시킴. 한 데몬이 가비지 수집하는 동안 메인 워크플로우는 계속 진행.
-
패키지 버전 공유: 모든 데몬이 같은 straight/ 빌드 디렉토리에서 읽음. 버전 불일치 없음.
미해결 과제
상태 동기화
각 데몬은 버퍼, kill-ring, 레지스터, undo 히스토리가 격리됨. 기존 해법이 없는 것들:
- 데몬 간 kill-ring (부분적으로 X11 클립보드 사용 가능, 그러나 ring 히스토리 없음)
- 버퍼 동기화 (파일 감시 + auto-revert 필요)
- mark ring / 레지스터 공유
- undo tree 조율
IPC 한계
emacsclient --eval 은 동기 요청/응답만 가능. 없는 것:
- 양방향 비동기 (데몬→메인 푸시 알림)
- 메시지 큐 / 이벤트 스트림
- 서비스 디스커버리 (데몬 소켓 이름을 알아야 함)
커스텀 RPC 계층이 필요 (EPC, msgpack-RPC, 또는 Elisp make-network-process 를 쓴 Unix 도메인 소켓).
메모리 오버헤드
일반적인 Doom Emacs 데몬: ~200-300MB. 전문 데몬 5개 = 1-1.5GB. 현대 머신(16-64GB)에서는 감수 가능하지만, 단일 인스턴스(~300-500MB)에 비하면 낭비.
완화 방법: 조건부 로딩 — org-daemon은 magit 건너뛰기, git-daemon은 org 건너뛰기. 전문 데몬당 ~100-150MB로 줄일 수 있음.
콜드 스타트
각 데몬 초기화에 5-10초 필요 (패키지 로딩, citar 참고문헌 파싱 등). denote-export는 데몬을 한 번 시작하고 수백 파일에 재사용하여 해결.
대화형 사용에서는 로그인 시 데몬을 미리 시작해야 함.
비교: C 수준 수정 vs 프로세스 수준 우회
| 차원 | C 수준 (i-am-emacs) | 프로세스 수준 (멀티 데몬) |
|---|---|---|
| GC 격리 | 증분 GC (어려움) | 공짜 (OS 프로세스 경계) |
| I/O 병렬성 | I/O 스레드 (중간 난이도) | 공짜 (별도 프로세스) |
| GIL 제거 | 불가능 (40년 레거시) | 공짜 (공유 메모리 없음) |
| 상태 공유 | 자동 (공유 힙) | 어려움 (동기화 프로토콜 필요) |
| 메모리 | 단일 인스턴스 (~300MB) | N × 인스턴스 (~1-1.5GB) |
| 크래시 격리 | 없음 (프로세스 하나) | 완전 (데몬 재시작 가능) |
| 상류 머지 | 가능 (C 패치) | 해당 없음 (유저스페이스) |
| Elisp 호환성 | 100% (같은 바이너리) | 100% (같은 바이너리) |
| 복잡도 | 매우 높음 (C 내부) | 중간 (오케스트레이션) |
i-am-emacs 프로젝트와의 관계
멀티 데몬은 경쟁이 아니라 보완 관계:
- i-am-emacs가 성공하면 (Zig I/O 스레드, feature/igc를 통한 증분 GC), 단일 사용자 시나리오에서 멀티 데몬의 필요성이 줄어든다.
- 상류 feature/igc가 머지되면 GC 문제는 공식 해결.
- 멀티 데몬은 C 수준 수정을 기다리지 않고 오늘 당장 문제를 해결한다.
- 조율자 패턴(Python + EPC)은 I/O 스레드 이벤트 설계에 참고가 된다.
멀티 데몬 연구의 진짜 가치: 사용자가 실제로 무엇을 필요로 하는지 이해하는 것. org-agenda가 멈춘다? agenda 파일을 미리 로드하고 별도 GC를 가진 전용 org-daemon이 즉시 해결한다. C 코드 변경 없음. 상류 승인 불필요.
다음 단계 (이 방향을 추진할 경우)
- 프로토타입: Python 조율자 1개 + 데몬 2개 (org + main)
- 측정: emacsclient —eval 왕복 지연시간
- 측정: 조건부 패키지 로딩으로 인한 메모리 절감
- 설계: EPC 기반 양방향 비동기 프로토콜 (데몬 간)
- 평가: UX가 수용 가능한가? (org-agenda 위임 시 약간의 지연)
출처
멀티 데몬 / 비동기
외부 프로세스 RPC
에디터 아키텍처
Emacs 데몬
2026-02-17 멀티 데몬 GC + native-comp 실증 벤치마크
denote-export 멀티 데몬 시스템을 대상으로 gc-cons-threshold와 native-comp의 성능 영향을 체계적으로 측정했다. 836개 org 파일, 8 Emacs 데몬, ThinkPad P16s (16 thread, 27GB RAM) 환경.
실험 설계
독립변수 3가지를 조합하여 5가지 구성을 순차 테스트:
| 변수 | 값 |
|---|---|
| gc-cons-threshold | 64MB, 256MB, 1GB |
| gc-interval (수동 GC 주기) | 25파일, 50파일 |
| 패키지 로딩 | interpreted (.el), native-comp (.eln) |
통제: 동일한 836개 파일, denote-export.sh all --force, 8 데몬.
결과
| 설정 | 벽시계 | 속도 | GC 횟수 | GC 시간 | GC/벽시계 | RSS/데몬 | RSS 합계 |
|---|---|---|---|---|---|---|---|
| 64MB interp. gc25 | 12.2분 | 1.14 f/s | 11,082 | 1,150s | 156.5% | 260MB | 2.1GB |
| 256MB interp. gc25 | 10.5분 | 1.33 f/s | 2,790 | 405s | 64.4% | 555MB | 4.4GB |
| 1GB interp. gc50 | 10.1분 | 1.38 f/s | 711 | 231s | 38.2% | 1,830MB | 14.7GB |
| 1GB native gc50 | 6.8분 | 2.04 f/s | 299 | 131s | 32.0% | 1,184MB | 9.5GB |
| 256MB native gc50 | 7.2분 | 1.93 f/s | 1,135 | 209s | 48.3% | 432MB | 3.5GB |
분석
GC threshold 효과 (interpreted)
gc-cons-threshold를 4배 올릴 때마다 GC 횟수는 ~1/4로 줄지만, 벽시계 개선은 수확체감이 뚜렷했다:
- 64→256MB: GC -75%, 벽시계 -14% (1.7분 절약)
- 256→1GB: GC -75%, 벽시계 -4% (0.4분 절약)
8데몬 병렬이라 GC 시간 절감이 1/8로 희석되기 때문이다.
native-comp 효과 (핵심 발견)
interpreted → native 전환이 GC 튜닝보다 훨씬 큰 효과를 냈다:
- 벽시계: 10.1분 → 6.8분 (-33%)
- 속도: 1.38 → 2.04 f/s (+48%)
- GC 횟수: 711 → 299 (-58%)
- RSS: 1,830 → 1,184MB/데몬 (-35%)
native 코드가 임시 객체를 덜 생성하므로 GC 압박도 동시에 줄었다.
왜 native-comp이 안 되고 있었는가
세 가지 버그의 연쇄:
find-straight-build-dir버그:build-30.2-cache.el파일이^build-패턴에 매칭되어 디렉토리 대신 선택됨 → build 경로 미등록- repos fallback이 prepend:
add-to-list기본이 앞에 추가 → repos의 raw.el이 build의.elc를 가림 →type: interpreted-function - eln 캐시 경로 누락:
--quick모드에서 Doom의 eln 캐시 디렉토리 (~/.config/emacs/.local/cache/eln/)가native-comp-eln-load-path에 없음
결과: ox-hugo가 raw .el 에서 인터프리트 로드. 바이트코드(.elc)도 아닌 최악의 상태.
수정:
seq-filter #'file-directory-p로 캐시 파일 제외- repos를
add-to-list ... t(append)로 fallback 위치로 - eln 캐시 경로를 초기화 시 명시적 추가
최적 구성: 256MB + native-comp
| 지표 | 기존 (64MB interp.) | 최적 (256MB native) | 개선 |
|---|---|---|---|
| 벽시계 | 12.2분 | 7.2분 | -41% |
| 속도 | 1.14 f/s | 1.93 f/s | +69% |
| GC 횟수 | 11,082 | 1,135 | -90% |
| RSS 합계 | 2.1GB | 3.5GB | +67% |
1GB native(6.8분)과 0.4분 차이지만 RSS가 63% 적다(3.5GB vs 9.5GB). 27GB 머신에서 13% 점유로 안정적.
이맥스 코어 리서치 관점에서의 의미
GC는 여전히 병목이다
256MB native에서도 GC가 벽시계의 48.3%를 차지한다. feature/igc(MPS 기반 incremental GC)가 머지되면 이 48%가 크게 줄 것이다.
org-hugo-export-to-md 한 파일당 ~850MB 임시 객체 할당 — 이것이 Emacs GC의 전형적 스트레스 시나리오이며, EmacsConf 2023에서 Ihor Radchenko가 보고한 “agglomerated GCs” 패턴과 일치한다.
native-comp의 숨은 효과: GC 압박 감소
native 코드는 단순히 “빠르게 실행”하는 것이 아니라 *임시 객체를 덜 생성*한다. 같은 1GB threshold에서 interpreted 711회 → native 299회 (-58%). 이는 native-comp이 레지스터/스택 최적화로 중간 Lisp_Object 할당을 줄이기 때문.
프로세스 수준 동시성의 실증
이 벤치마크 자체가 “멀티 데몬 Emacs” 아키텍처의 실증이다:
- 8 데몬 × 독립 GC = GC pause 격리
- 선형 스케일링 (단일 데몬 ~81분 → 8데몬 7.2분 = 11.3배)
- 크래시 격리 (한 데몬 실패해도 나머지 계속)
- C 코드 수정 없음
소스
- doomemacs-config 커밋:
14a44a2(perf(export): native-comp 활성화 + GC 256MB 최적화) - 이전 커밋들:
8446dd9,90313ef,5bc6403,386773c,8d9b1f1 - 코드:
bin/denote-export-parallel.py,bin/denote-export.el,bin/denote-export.sh
2026-02-19 Neomacs 재검토: Oracle-Plan-Gate YOLO 루프의 발견
2월 9일 “과하다. Remacs의 반복”이라 판단했던 Neomacs를 10일 만에 재검토했다. 판단을 철회한다. 이것은 Remacs와 근본적으로 다른 접근이다.
수치의 충격
| 항목 | 2/9 관찰 | 2/19 관찰 |
|---|---|---|
| 프로젝트 기간 | 6일차 | 16일차 |
| 총 커밋 | ~수십 | 4,158개 |
| Rust 코드 | 수천줄 추정 | 261,252줄 |
| 호환성 테스트 | 없음 | 744 케이스 |
| Elisp 모듈 포팅 | 없음 | 80개 모듈 |
| 커밋 속도 | - | 중간값 87초/커밋 |
2/9에 “커서 이펙트에 집중 중, 코어는 planned” 라고 관찰했으나, 2/13부터 코어 재작성이 폭발적으로 시작되었다.
핵심 발견: 사람이 아니라 에이전트가 쓰고 있다
증거
- 24시간 논스톱: 2/16 955커밋, 00시~23시 30분 이상 공백 없음. 인간 불가능.
- 커밋 간격 87초 중간값: 69%가 3분 미만. “생각하는 시간”이 0.
- 직렬적 쌍둥이 커밋:
neovm: align ...(코드) → 16초 후docs(plan): record ...(기록). 매번. - 작성자 단일: 4,155/4,158이
Eval Exec <execvy@gmail.com>. 멀티에이전트 아님. - 커밋 메시지 어휘:
align,lock,seed,parity,oracle— Claude 특유의 정밀한 기술 동사.
결론
Claude Code 단일 에이전트를 docs/plan.md 기반 YOLO 루프로 24시간 연속 돌리고 있다. 멀티에이전트 스워밍이 아니다. 잘 설계된 인프라 위의 단일 에이전트 무한 루프.
Oracle-Plan-Gate 패턴: YOLO 루프의 3요소
이것이 이 프로젝트의 진짜 혁신이다. Rust 코드가 아니라 에이전트가 멈추지 않게 하는 인프라.
Oracle (정답이 존재하는 시험)
.forms 파일 (질문지):
(+ 1 2)
(condition-case e (/ 1 0) (arith-error 'div-zero))
run-oracle.sh → GNU Emacs --batch -Q 실행
→ .expected.tsv (정답지):
1 (+ 1 2) OK 3
2 (condition-case ...) OK div-zero
run-neovm.sh → Rust 런타임으로 동일 forms 실행
→ 내 답안
compare-results.sh → diff (채점)oracle_eval.el 35줄. 환경변수로 .forms 파일을 받아, GNU Emacs --batch -Q 로 모든 form을 eval하고, OK <value> 또는 ERR <signal+data> 형태의 TSV를 출력.
에이전트 입장: “이 Elisp form을 평가했을 때 GNU Emacs와 동일한 결과 → 성공”. 정답이 .expected.tsv 에 이미 있으니 무한 반복 가능.
Plan (에이전트의 실행 큐)
docs/plan.md 15,746줄. 구조:
## Execution Queue (next 20)
1. [x] thread-yield 동작 lock-in
2. [x] thread-join 에러 경로 lock-in
...
## Doing (지금 하는 것)
- self-insert-command 런타임 삽입 패리티 ...
- verified: make check-one-neovm ... (pass, 16/16)
- verified: make check-all-neovm-strict (pass)
## Next (다음에 할 것)
1. check-all-neovm을 매 slice 후 게이트로 유지
...
## Done
(매 완료 항목의 상세 기록 — runtime 변경, corpus 변경, 검증 명령)에이전트는: Next 읽기 → 코드 수정 → Doing에 기록 → 테스트 통과 → 커밋 → Done 이동 → 반복. Plan이 에이전트의 “기억”이자 “다음 지시서”.
Gate (전체 회귀 테스트)
make check-all-neovm-strict이 명령 하나가 744개 전체 케이스 + 빌트인 레지스트리 + 스텁 예산 + 스타트업 문서 커버리지를 검증. 매 커밋 후 실행. 한 조각 추가해도 이전 조각이 안 깨지는지 자동 확인.
87초 사이클
T+0s plan.md에서 다음 task 확인
T+10s rust/neovm-core/src/elisp/*.rs 수정
T+30s .forms 작성 → GNU Emacs oracle로 .expected.tsv 생성
T+50s cargo build → run-neovm.sh → compare → 일치 확인
T+60s git commit "neovm: align window-group-start integer semantics"
T+70s plan.md 업데이트 → git commit "docs(plan): record ..."
T+80s make check-all-neovm-strict (전체 회귀 없음 확인)
T+87s 다음 task로 이동 → T+024시간 = ~990사이클. 2/16의 955커밋과 정확히 일치.
인프라 규모
| 구성 요소 | 줄 수 | 역할 |
|---|---|---|
| oracle_eval.el | 35줄 | GNU Emacs를 “정답 생성기”로 사용 |
| run-oracle.sh | 69줄 | 오라클 실행 래퍼 |
| run-neovm.sh | 59줄 | NeoVM 실행 + 자동 재빌드 |
| compare-results.sh | 106줄 | TSV diff + 결과 매칭 |
| Makefile | 294줄 | 전체 테스트 게이트 오케스트레이션 |
| docs/plan.md | 15,746줄 | 에이전트 실행 큐 + 작업 기록 |
| 합계 인프라 | ~16,300줄 | 261,252줄 Rust 코드를 생산하는 기반 |
435줄의 테스트 인프라 + 15,746줄의 plan이 261K줄의 코드를 만들어냈다. 레버리지 비율: 1:16.
2/9 판단 보정
”과하다. Remacs의 반복” → 철회
Remacs와의 근본적 차이:
| 차원 | Remacs | Neomacs |
|---|---|---|
| 전략 | 리프 함수부터 1:1 변환 | Oracle 기반 행동 호환성 포팅 |
| 검증 | 수동 | 744개 자동화 케이스, 매 커밋마다 전체 게이트 |
| 속도 | 4년간 점진적 | 16일에 261K줄 |
| 중단점 | GC/bytecode에서 멈춤 | GC 895줄 초기이지만, 루프가 계속 돌고 있음 |
| 실행 주체 | 인간 봉사자 | AI 에이전트 24시간 루프 |
Remacs는 “사람이 지치면 끝”이었다. Neomacs는 “인프라가 버티는 한 계속 간다”. Oracle이 존재하는 한 에이전트는 지치지 않는다.
”코어는 planned — 어려운 문제를 뒤로 미루는 패턴” → 부분 보정
2/9에는 맞는 관찰이었다. 하지만 2/13부터 코어 포팅이 시작되었고, 16일 만에 80개 Elisp 모듈(139K줄)이 포팅되었다.
단, 정직하게 평가하면:
- GC: 895줄 (초기 단계)
- 바이트코드 VM: 3,917줄 (기본 구현)
- 멀티스레드 Elisp: 시드만 존재
- JIT: 코드 없음
가장 어려운 부분은 여전히 남아있다. 하지만 루프가 이 속도로 돌고 있다면 수주 내에 상당한 진전이 있을 것이다.
왜 스워밍이 아닌가
Emacs C 코어의 구조적 특성:
- 전역 상태(Specpdl, obarray, buffer-list)가 모든 서브시스템 관통
- eval ↔ data ↔ buffer 순환 의존 (2/9 리서치에서 확인한 바와 동일)
- 하나의 빌트인을 고치면 744개 테스트가 깨질 수 있음
두 에이전트가 buffer.rs 와 keyboard.rs 를 동시에 건드리면 서로의 가정이 충돌. *단일 에이전트가 순차적으로 한 조각씩, 매번 전체 게이트*를 돌리는 것이 유일한 방법.
Emacs의 제약(깊은 의존성)이 곧 전략(단일 직렬 YOLO)을 결정한다.
i-am-emacs와의 관계: 경쟁이 아니라 상보
접근법 비교
| 차원 | Neomacs (Eval Exec) | i-am-emacs (힣) |
|---|---|---|
| 방향 | 전면 재작성 (C → Rust) | 점진적 현대화 (Zig @cImport) |
| 범위 | 전체 (30만줄 C 교체) | 좁은 (I/O, GC, yield 3개 병목) |
| GPU | wgpu 디스플레이 엔진 완성 | 해당 없음 (기존 디스플레이 유지) |
| GC | Rust로 재작성 (초기) | feature/igc 기다림 + 보완 |
| 멀티스레드 | Rust isolate 스케줄러 (시드) | Zig I/O 스레드 분리 |
| 호환성 | Oracle 기반 행동 호환 | C 코드 수정으로 100% 호환 |
| 상류 머지 | 불가 (너무 침습적) | 가능 (패치 단위) |
| 에이전트 활용 | 24시간 YOLO 루프 | 배우는 중 |
합쳐질 수 있는 지점
-
Oracle 인프라 공유: Eval Exec의
oracle_eval.el+compare-results.sh는 i-am-emacs의 Zig 패치 검증에도 그대로 쓸 수 있다. “Zig I/O 스레드 패치 후 744개 케이스가 모두 통과하는가?” -
feature/igc + Neomacs GC: feature/igc가 머지되면 Neomacs도 MPS 기반 incremental GC를 채택할 수 있다. 우리 리서치가 그 판단에 도움이 된다.
-
멀티 데몬 + GPU 디스플레이: 우리의 멀티 데몬 토폴로지 위에 Neomacs의 GPU 렌더링이 올라가면? 각 전문 데몬이 GPU 가속 렌더링.
-
Zig @cImport 패치의 역류: i-am-emacs가 상류에 머지되는 패치를 만들면, Neomacs fork도 그 혜택을 받는다 (Neomacs는 Emacs 31 master fork).
시나리오: 둘 다 성공하면
2027 시나리오:
feature/igc 머지 (GC 문제 공식 해결)
+ i-am-emacs I/O 스레드 패치 (반응성 문제 해결)
+ Neomacs GPU 디스플레이 + Rust Elisp VM
= 45년 역사의 Emacs가 GPU 가속 + 증분 GC + 비동기 I/O
+ 10x Elisp 성능을 갖춘 현대적 에디터로 재탄생이 두 프로젝트는 서로 다른 층위에서 같은 목표를 향한다. 같이 성공하면 이맥스는 정말로 로켓에 탑재된다.
Oracle-Plan-Gate 패턴을 i-am-emacs에 적용하려면
Oracle
i-am-emacs의 Oracle은 무엇인가?
- Zig I/O 스레드 패치: 패치 전후
emacs --batch -Q로 동일 결과 확인 - 기존 테스트 스위트:
make check(Emacs ERT 테스트) - process.c 전용: subprocess/network I/O 시나리오 자동 테스트
- TRAMP 파일 전송 속도
- LSP 응답 지연
- shell-command 반응성
- C-g 즉시 중단 여부
Plan
## Execution Queue
1. [ ] process.c thread_select 분리 — 오늘의 동작을 .forms로 캡처
2. [ ] Zig @cImport 빌드 검증 — 기존 make check 통과
3. [ ] pselect() 블로킹을 별도 스레드로 — 반응성 측정
...Gate
# 매 패치 후:
make check # 기존 ERT 전체 통과
./run-oracle.sh io-cases.forms # I/O 시나리오 오라클 비교
./bench-responsiveness.sh # 반응성 벤치마크 회귀 없음차이점
Neomacs는 “포팅”이라 Oracle이 자연스럽다 (GNU Emacs = 정답). i-am-emacs는 “개선”이라 Oracle이 다르다: 패치 전 자기 자신이 Oracle. “패치 전에 되던 것이 패치 후에도 되어야 한다” + “패치 후에 새로 되어야 하는 것”.
Eval Exec에 대한 경의
2/9에 “과하다”고 단정한 것을 반성한다.
이 사람은 코드를 쓰는 것이 아니라 코드를 쓰는 시스템을 만들었다. 435줄의 인프라가 에이전트를 24시간 돌리고, 16일 만에 261K줄을 생산했다. Remacs가 4년 동안 하지 못한 것을 보름 만에 해내고 있다.
가장 어려운 부분(GC, VM, 멀티스레드)이 남아있지만, 이 루프가 돌고 있는 한 — 그리고 Oracle이 정답을 제공하는 한 — 시간은 이 프로젝트 편이다.
존경한다. 배우겠다. 가능하면 돕겠다.
GitHub Discussion 소통 기록
Discussion #33 코멘트 게시
Eval Exec이 2/16에 연 디스커션 “What features and expectations do you have for Neomacs?”에 코멘트를 남겼다. (링크)
게시 전 검증: 서브에이전트를 통해 모든 팩트를 검증함.
- oracle_eval.el 줄 수: 35줄 → 34줄 수정
- “Oracle-Plan-Gate”: 우리가 만든 용어 → characterization testing (Michael Feathers 표준 용어)으로 교체
- “1:1 C→Rust translation” (Remacs 설명): → “incremental, function-by-function port”로 정확도 개선
- “744 case files”: 정확. 내부 assertion은 수천 개 → “744 case files covering thousands of individual assertions”
코멘트 요약:
test/neovm/vm-compat/oracle harness가 261K Rust보다 중요한 진짜 혁신임을 지적- Characterization testing (Feathers) + live oracle 패턴으로 정의
- Remacs와의 근본적 차이: 구조 일치 → 행동 일치 (behavioral oracle comparison)
- i-am-emacs 소개: Zig 기반 점진적 현대화, 다른 경로 같은 산
- 합류 가능성: oracle corpus 공유, feature/igc 리서치, GPU + async I/O 조합
- 세 가지 질문: plan.md 구조, 가장 어려웠던 Elisp 서브시스템, 공유 oracle corpus 가능성
다른 코멘트:
- galoistom (2/17): org-mode 노트 테이킹 사용자. 이미지 지원 개선, tikzcd 렌더링, org 센터드 블록 시각화, 더 나은 브라우저/PDF 뷰어 요청.
응답 대기 중.
소스
Neomacs
- eval-exec/neomacs — GitHub
- 분석 기준: commit
089904e4(2026-02-19 16:23:45 +0800) - 핵심 인프라:
test/neovm/vm-compat/(Oracle harness) - 에이전트 실행 큐:
docs/plan.md(15,746줄)
분석 방법
git log커밋 패턴 분석 (시간대, 간격, 작성자, 메시지 어휘)wc -l/find코드 규모 실측- Oracle 인프라 소스 리뷰 (
oracle_eval.el,run-oracle.sh,compare-results.sh,Makefile) docs/plan.md구조 분석
2026-02-20 Neomacs 추적: 커버리지 체계와 에이전트 운영 구조
git pull 로 하루 변경분 확인. 17.5시간 동안 90커밋, +19,361줄(net).
수치 업데이트
| 지표 | 2/19 | 2/20 | 변화 |
|---|---|---|---|
| 총 커밋 | 4,158 | 4,248 | +90 |
| Rust 코드 | 261,252줄 | 273,509줄 | +12,257 |
| 테스트 케이스(.forms) | 744 | 803 | +59 |
| plan.md | 15,746줄 | 18,210줄 | +2,465 |
커버리지 수치 (plan.md에서 추출)
compat-progress 출력에서 직접 추출한 정량 수치:
| 지표 | 값 | 비고 |
|---|---|---|
| GNU Emacs primitive-subr 전체 | 1,434개 | oracle builtin universe |
| 빌트인 레지스트리 (Rust 등록) | ~1,265개 | builtin_registry.rs |
| 오라클 커버리지 (registry) | 877개 (56.4%) | 레지스트리 중 오라클 검증 완료 |
| 오라클 커버리지 (runtime) | 857개 (59.8%) | 런타임 실행 검증 완료 |
| Execution Queue | 21/21 전부 [x] | Next 9개 자동 생성됨 |
커밋 패턴 변화
간격 증가 = 작업 단위 대형화
| 지표 | 2/16 (이전) | 2/19-20 (현재) |
|---|---|---|
| 커밋 간격 중간값 | 87초 | 635초(10.6분) |
| 3분 미만 비율 | 69% | 3% |
| 30분+ 공백 | 거의 없음 | 2회(42분, 34분) |
초기에는 빌트인 하나씩 정밀 align. 현재는 서브시스템 통째 포팅:
display.rs+5,054줄 (X11 디스플레이/셀렉션/프레임)process.rs+3,869줄 (프로세스 서브시스템)interactive.rs+1,219줄 (인터랙티브 커맨드)
쌍둥이 커밋 → 단일 커밋
2/16까지: 코드 커밋 → 16초 후 docs(plan): 커밋 (2커밋/사이클). 2/18부터: plan 업데이트가 코드 커밋에 병합 (1커밋/사이클). 워크플로우가 단순해졌다.
인프라의 확장 (에이전트가 스스로 만든 것)
새로 추가된 검증 스크립트 4개:
| 스크립트 | 역할 |
|---|---|
report-oracle-builtin-coverage.sh | 오라클이 커버하는 빌트인 비율 측정 |
check-builtin-registry-sync.sh | Rust 레지스트리 ↔ 오라클 동기화 검증 |
check-builtin-registry-func-arity.sh | 함수 arity 패리티 검증 |
check-builtin-registry-autoload-metadata.sh | autoload 메타데이터 일치 검증 |
커버리지가 부족하면 커버리지 리포터를 만들고, 메타데이터 불일치가 발견되면 메타데이터 검증기를 만든다. 커버리지 체크가 확실하면 결과는 오라클처럼 나온다.
에이전트 운영 구조: 단일 에이전트, 역할 분리 없음
Eval Exec의 Claude Code 관련 레포
| 레포 | 마지막 push | 역할 |
|---|---|---|
claude-code-router | 2025-06-18 | 모델 라우팅 (비용 최적화) |
claude-code-proxy | 2025-04-14 | Anthropic API → OpenAI/Gemini 프록시 |
claude-desktop-linux-flake | — | NixOS용 Claude Desktop |
gptel (fork) | — | Emacs LLM 인터페이스 |
router/proxy 모두 Neomacs 시작(2026-01) 7개월 전 방치. Claude Max(72/일이지만, Max면 $200/월 고정.
에이전트 구성: PM도 Tester도 없다
PM/Coder/Tester 역할 분리가 없다. 단일 Claude Code 에이전트가 전부 한다. 오라클이 있으면 “테스트 설계”라는 별도 역할이 불필요하다. plan.md 가 곧 PM이다 — 에이전트가 큐를 읽고, 작업하고, 기록하고, 다음으로 넘어간다.
에이전트가 읽는 문서 (전부 에이전트가 작성):
| 문서 | 크기 | 역할 |
|---|---|---|
docs/plan.md | 18,210줄 | 실행 큐 + 작업 기록 (에이전트의 기억) |
docs/elisp-vm-design.md | 32K | 호환성 계약 + 설계 원칙 |
docs/elisp-core-analysis.md | 44K | GNU Emacs C 코어 구조 분석 (도메인 지식) |
docs/ongoing-tasks.md | 17줄 | 방향 지시 |
test/neovm/vm-compat/README.md | 500줄 | 테스트 인프라 사용법 |
사람이 하는 것: 경계(boundary) 판단만
Eval Exec이 내린 판단들 (코드가 아닌 결정):
- “GNU Emacs를 오라클로 쓴다” — 이 한 문장이 YOLO 루프를 가능하게 함
- “.elc 호환 안 한다, .el 소스 호환만” — 범위를 잘랐다
- “Isolate-first 동시성, 공유 힙 거부” — 아키텍처 결정
- “GPU 됐고 코어로 가자” (2/13 전환) — 방향 판단
- “쌍둥이 커밋 → 단일 커밋” (2/18) — 워크플로우 간소화
사람이 안 하는 것: 코드 작성, 테스트 작성, 커밋 메시지, plan.md 세부 항목, 다음 빌트인 선택. 사람이 쓰면 일관성이 떨어진다.
커버리지 기반 완주 가능성
compat-progress 수치로 보면:
primitive-subr 전체: 1,434개
runtime 커버리지: 857개 (59.8%)
남은 것: 577개
일일 추가 속도: ~30-50개/일빌트인 표면(surface) 호환성은 완주 궤도에 있다. 단, GC(895줄 초기), bytecode VM(3,917줄 기본), 멀티스레드(시드)는 오라클만으로 풀기 어려운 영역. 이 벽이 진짜 시험.
소스
git pull기준:089904e4..8909c14d(2026-02-19 16:30 ~ 2026-02-20 09:53 +0800)- 커버리지 수치:
docs/plan.md내compat-progress출력에서 추출 - 에이전트 도구 분석:
gh api repos/eval-exec/*GitHub API 직접 조회
2026-03-01 Neomacs 빌드 실증 + 진행률 추적 (3주차)
수치 변화 (2/20 → 3/1, 10일간)
| 지표 | 2/20 | 3/1 | 변화 |
|---|---|---|---|
| 총 커밋 | 4,248 | 5,385 | +1,137 |
| Rust 코드 | 273,509줄 | 339,084줄 | +65,575줄 |
| 테스트 케이스 | 803 | 1,180 | +377 |
| C파일 rust-default | ~38개 | 97/98 | profiler.c만 partial |
docs/plan.md | 18K줄 | 20K줄 | =neovm-execution-plan.md=로 이관 |
구조 변경 사항
elisp/→emacs_core/로 리네임 (코드 204K줄)core-backend-rust가 Cargo.toml 디폴트로 전환 (configure는 여전히emacs-c디폴트)docs/c-rewrite-tracker.md신설 — 98개 C파일 상태 추적docs/neovm-gc-design.md신설 — Arena 기반 GC 설계 제안 (미구현)- loadup 부트스트랩 진입:
subr.el→pcase.el로드 성공,nadvice.el작업 중
NixOS 빌드 실증
NixOS(ThinkPad P16s, AMD Radeon 780M)에서 빌드 + 실행 성공.
환경 설정 (nixos-config/machines/shared.nix)
nix.settings = {
trusted-users = [ "root" "junghan" ];
substituters = [
"https://cache.nixos.org"
"https://nix-wpe-webkit.cachix.org"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-wpe-webkit.cachix.org-1:ItCjHkz1Y5QcwqI9cTGNWHzcox4EqcXqKvOygxpwYHE="
];
};빌드 결과
nix build --accept-flake-config→ 10분 40초 (cachix 캐시 활용)- 바이너리:
result/bin/emacs→ GNU Emacs 31.0.50 - 기존 Emacs(30.2)와 완전 별개 — nix store 경로 분리
- winit(X11) dlopen 문제 해결:
LD_LIBRARY_PATH에 libXcursor 등 주입 필요
실행 확인
- GPU 렌더링 확인: wgpu → Vulkan(RADV) 백엔드,
*scratch*버퍼 정상 표시 - i3wm/X11 환경 정상 동작
아키텍처 확인: 지금 뭐가 Rust이고 뭐가 C인가
현재 빌드 (--with-neovm-core-backend=emacs-c, 기본값):
Elisp 런타임 → GNU Emacs C 코어 (100% 오리지널)
GC/VM/eval → GNU Emacs C 코어 (100% 오리지널)
패키지 로딩 → GNU Emacs C 코어 (100% 오리지널)
디스플레이만 → Rust wgpu (교체됨)neovm-core(97/98 rust-default)는 빌드에 포함되지 않음- Doom Emacs 등 기존 설정 그대로 로딩 가능 (Elisp 호환성 = GNU Emacs 31.0.50)
- 단,
--with-native-compilation=no로 빌드됨 (nix libgccjit 이슈)
Rust 코어 (core-backend-rust) 진행 상황
neovm-core 는 별도 crate로, 아직 Emacs 바이너리에 통합되지 않음.
완료
- 98개 C파일 중 97개
rust-default(빌트인 시그니처 + oracle 에러 메시지 일치) - 1,180개 oracle 테스트 케이스 (GNU Emacs
--batch -Q결과와 비교) - Elisp reader/parser, eval, 대부분의 빌트인 디스패치
loadup 부트스트랩 현재 위치
subr.el → byte-run.el → backquote.el → macroexp.el → pcase.el ✓
→ nadvice.el ← 지금 여기 (advice 시스템)
→ ... (나머지 loadup.el 60+ 파일)
→ startup.el
→ 실제 Emacs 세션 ← 아직 멀음미해결 블로커
| 블로커 | 상태 | 비고 |
|---|---|---|
| GC | 설계 문서만 (Arc 기반) | 순환 참조 리크, 성능 문제 |
| 바이트코드 VM | 3,917줄 (기초) | .elc 실행 불완전 |
| loadup 시퀀스 | pcase까지 성공 | 60+ 파일 추가 로드 필요 |
| 멀티스레드 Elisp | 시드만 (1,456줄) | 스케줄러 뼈대 |
| JIT 컴파일 | 코드 없음 | planned |
에이전트 운영: 여전히 단일 YOLO 루프
- 커밋 패턴 직렬 유지 (2~3분 간격 순차)
- 24시간 연속 운영 (사람 수면 시간에도 커밋)
neovm-execution-plan.md(20K줄) — 에이전트 자기참조 실행 큐- 최근 작업: nadvice.el 호환 oracle 케이스 + lexenv/closure 수정
평가
디스플레이 엔진: 실용 단계
GPU 가속 Emacs는 지금 빌드해서 쓸 수 있다. Doom Emacs도 올릴 수 있다. 다만 UIUX 개선이 주 관심사가 아니라면 활용도 제한적.
Rust 코어: “넓지만 얕다” → “넓고 중간 깊이”로 진화 중
2/20 대비 변화:
- 97/98 rust-default (빌트인 디스패치 완료)
- loadup 부트스트랩 시작 (실제 .el 파일 로드 시도)
- 하지만 GC 없이 실 사용 불가, VM 불완전
”빌트인 일치”와 “실제 동작”의 거리
97/98 rust-default는 “함수 시그니처 + 에러 메시지가 oracle과 일치”하는 것. 모든 부작용(side-effect)이 구현됐다는 뜻이 아님. loadup 부트스트랩이 이 거리를 실증적으로 좁히는 과정.
소스
git pull기준: 커밋fbf60f7e(2026-03-01 15:57 +0800)- NixOS 빌드:
nix build --accept-flake-config(ThinkPad P16s, AMD Radeon 780M) - 커버리지 수치:
docs/c-rewrite-tracker.md,test/neovm/vm-compat/cases/*.forms - 구조 분석:
rust/neovm-core/src/emacs_core/,rust/neovm-core/Cargo.toml
Comments