enode — 코드 구조
f46e187시점의 계측. 수치는 전부 이 저장소에서 직접 재었다.
전체 계측
| 항목 | 값 |
|---|---|
| Go 파일 | 100 (비테스트 67 · 테스트 33) |
| 총 LOC | 27,211 (비테스트 17,288 · 테스트 9,923) |
| 패키지 | 15 (cmd/ 5 · internal/ 10) |
| Go 버전 | go 1.26 하한 · toolchain go1.26.6 |
| 모듈 | github.com/taeels/enode |
| Makefile | 없음. 코드 생성 없음. 벤더링 없음 |
디렉터리 배치
enode/
├── cmd/ 실행파일 다섯
│ ├── enode/ 노드 상주 프로세스 3 파일 269줄
│ ├── enodectl/ 한 기계의 인스턴스 관리 4 파일 531줄
│ ├── mediator/ 중앙 서버 2 파일 405줄
│ ├── runctl/ 사용자 CLI (무상태) 1 파일 423줄
│ └── iapadapter/ It's a Plan 어댑터 7 파일 2,436줄
├── internal/ 라이브러리 열
│ ├── api/ Mediator HTTP 표면 1+1 4,172줄
│ ├── store/ PostgreSQL 상태 저장소 12+2 4,787줄
│ ├── contract/ 계약 · 광고 타입 5+3 3,565줄
│ ├── enode/ 노드 런타임 25+20 8,254줄
│ ├── match/ 요구 → 노드 (순수 함수) 1+1 408줄
│ ├── record/ Run Record (파일시스템) 1+1 676줄
│ ├── schema/ 형식 전용 JSON Schema 1+1 339줄
│ ├── config/ Mediator 설정 (YAML) 2+1 481줄
│ ├── runctl/ runctl HTTP 클라이언트 1+1 345줄
│ └── build/ 버전 한 줄 1+1 120줄
├── packaging/ deb · rpm · msi · macos
├── scripts/testdb.sh 테스트용 Postgres 도커
└── .github/workflows/ ci.yml · package.yml · release.yml
파일 수는 비테스트+테스트 표기다. 줄 수는 테스트 포함이다.
파일 분류
실행 진입점 (5)
cmd/*/main.go 다섯. 전부 [no test files] 상태다.
cmd/mediator/main.go— 설정 로드 → 토큰 부트스트랩 →store.Open→
Migrate→ reaper 고루틴 →http.Server.WriteTimeout을 일부러 걸지
않는다(claim 롱폴 최대 2시간)cmd/enode/main.go— 신원 유도 → 로컬 잠금 → 광고 고루틴 + claim 워커
고루틴 두 개.--once·--ready-file이 탄력 노드용cmd/enodectl/main.go— 프로세스 관리. 셸판이node_id를 다시 계산하던 것을
없애려고 Go 로 옮겼다cmd/runctl/main.go— 무상태.permute()가 플래그를 앞으로 모아
runctl submit x.json --wait같은 순서를 받는다cmd/iapadapter/main.go— 바깥(이슈 트래커)을 아는 유일한 부품
도메인 어휘 (1 패키지)
internal/contract — 넷의 공통 어휘. contract.go(1,724줄) · grammar.go ·
checkplan.go · planshape.go · advert.go.
순수 함수 (2 패키지)
I/O 가 0인 것 둘. 테스트하기 가장 쉽고 실제로 가장 잘 덮여 있다(97.5% · 75.7%).
internal/match— 요구 대 광고를 받아 배정표 또는 거절 코드를 돌려준다internal/schema— 형식 전용 JSON Schema 검증기.CheckBoundary가
minimum·minLength같은 품질 판정 키워드를 계약 단계에서 거절한다
상태와 영속 (2 패키지)
internal/store— PostgreSQL. 12개 비테스트 파일이 동사별로 갈려 있다:
store.go(생성 · 열기) ·claim.go(배분 · 보고) ·acquire.go·
release.go·dispatch.go·expand.go·rollback.go·seal.go·
observe.go·ask.go·reap.go·verdict.go. 매칭 로직은 여기 없다internal/record— Run Record. DB 가 아니라 파일시스템(chmod 0444/0555
봉인) · blob · tar
표면 (2 패키지)
internal/api— 15개 라우트가api.go한 파일(830줄)에 전부 있다internal/runctl— runctl 의 HTTP 클라이언트. 12개 메서드
노드 런타임 (1 패키지, 25 파일)
internal/enode 가 이 저장소에서 파일 수가 가장 많은 패키지다.
신원 · 잠금 identity.go repoid.go lock.go lock_unix.go lock_windows.go
탐지 · 광고 detect.go advertise.go leases.go
실행 claim.go(873줄) runner.go argv.go agent.go
하네스 harness.go claude.go
환경 env.go config.go paths.go
워크스페이스 workspace.go setup.go diff.go changed.go
수거 collect.go hook.go
디스크 disk_unix.go disk_windows.go
OS 분기는 _unix.go / _windows.go 빌드 태그 쌍 둘(lock · disk)로만
처리한다.
설정과 부수 (2 패키지)
internal/config— Mediator 설정(YAML). 사용자 경로가/etc를 이긴다internal/build—<cmd> --version한 줄. 링커가 박거나 Go VCS 스탬프에서
읽는다
내부 의존 그래프
단방향이고 순환이 0건이다.
flowchart TD
CMDMED["cmd/mediator"]
CMDEN["cmd/enode"]
CMDECTL["cmd/enodectl"]
CMDRC["cmd/runctl"]
CMDIAP["cmd/iapadapter"]
API["internal/api"]
ST["internal/store"]
ENL["internal/enode"]
RCL["internal/runctl"]
CT["internal/contract"]
MT["internal/match"]
RD["internal/record"]
SC["internal/schema"]
CF["internal/config"]
BD["internal/build"]
CMDMED --> API
CMDMED --> ST
CMDMED --> RD
CMDMED --> CF
CMDMED --> BD
CMDEN --> ENL
CMDEN --> BD
CMDECTL --> ENL
CMDECTL --> BD
CMDRC --> RCL
CMDRC --> BD
API --> ST
API --> MT
API --> RD
API --> SC
API --> CT
API --> CF
ST --> CT
ST --> MT
ST --> RD
ENL --> CT
MT --> CT
cmd/iapadapter 는 그래프에 간선이 없다. 내부 패키지를 하나도 import 하지 않고
자기 안에서 HTTP 로 Mediator 와 말한다.
경계 — internal/enode 는 internal/store 와 internal/api 를 못 본다
이 저장소에서 가장 중요한 구조적 사실이다.
internal/enode ──import──▶ internal/contract (import 하는 것은 이것뿐)
internal/enode import 안 함 internal/store
internal/enode import 안 함 internal/api
노드가 Mediator 내부를 못 보는 것이 규약이 아니라 컴파일러가 지키는 사실이다.
노드는 Mediator 를 HTTP 표면으로만 안다. 노드 코드에서 Mediator 의 상태 표현을
직접 만지려는 시도는 컴파일이 안 된다.
같은 방향으로 internal/store 도 internal/api 를 모른다. 의존은 언제나
바깥(표면)에서 안(상태)으로만 흐른다.
코드 패턴
doc comment 가 「왜」를 적는다
이 저장소에서 가장 두드러지는 성질이다. 거의 모든 패키지 · 타입 · 함수에 doc
comment 가 붙어 있고, 그 내용이 「무엇을 한다」가 아니라 왜 이 형태인가 · 어떤
대안을 왜 기각했나 · 어떤 실측이 이것을 고치게 했나다. 실측 사례가
식별자(vm-scratch-7 · zephyr-setup-8 · colima-enode-1 · third-run-1 ·
reject-3 · rewind-1)와 함께 인용되는 자리가 20곳 이상이다.
internal/store/schema.sql 은 스키마 파일이라기보다 주석이 절반 이상인 설계
문서에 가깝다 — 각 컬럼이 왜 있는지, CHECK 을 왜 안 거는지가 적혀 있다.
ADR-NNN 을 코드에 인용한다
ADR-\d{3} 이 Go 소스에서 55개 서로 다른 번호로 인용된다. 결정과 코드
사이의 역추적이 grep 하나로 된다.
오류를 삼킬 때 이유를 적는다
//nolint: 지시자 28곳은 전부 errcheck 이고 거의 다
defer f.Close() · defer tx.Rollback(ctx) 형태이며, 여럿에 이유 주석이 붙어
있다. _ = 로 오류를 버리는 39곳도 대부분 옆에 근거가 적혀 있다.
「자리만 파 둔 것」을 주석으로 표시한다
TODO 를 안 쓰는 대신 doc comment 로 적는다.
internal/store/seal.go:95ByMediator = "mediator"— "아직 안 쓴다",
실제 참조 0건internal/enode/env.go:60RunIdentity— "지금은 아무도 안 본다. 자리를
파는 이유는…"(R2요청자 신원용)internal/enode/env.go:88Transparent{}— 아무것도 주입 안 하는
Credentials구현. 의도적으로 nil 대신 타입을 둔 것을 주석이 적는다
큰 함수 · 큰 파일
| 함수 | 줄 | 위치 |
|---|---|---|
applyExpands |
273 | internal/store/expand.go:52 |
run |
250 | cmd/runctl/main.go |
Worker.execute |
222 | internal/enode/claim.go:383 |
buildPrompt |
210 | internal/enode/agent.go:186 |
Verify |
194 | internal/store/verdict.go:98 |
runSetup |
185 | cmd/mediator/setup.go |
RenewLeases |
182 | internal/store/claim.go:38 |
main |
173 | cmd/enode/main.go |
Contract.Validate |
612 | internal/contract/contract.go:954 |
| 파일 | 줄 |
|---|---|
internal/api/api_test.go |
3,342 (테스트 78개 단일 파일) |
internal/contract/contract.go |
1,724 |
internal/store/claim.go |
938 |
internal/enode/claim.go |
873 |
internal/api/api.go |
830 |
표기 규약 — 저장소 고유
CLAUDE.md(72줄)가 두 규약을 세운다.
- 장식 문자를 안 쓴다. 강조는 마크다운
**굵게**만. 코드 · 주석 ·
문자열 · 로그 · 테스트 메시지 · 프롬프트 · 설정 어디에도 넣지 않는다.
CI 가 집행하는 것은 U+2605(검은 별) 하나뿐이고 현재 0건이다 - 밖으로 나가는 것은 영어, 안 나가는 것은 한국어. 기준이 「코드냐」가
아니라 「밖으로 나가느냐」다 — 에러 문자열 · 로그 · CLI 출력 · 테스트 이름 ·
픽스처 · 에이전트 프롬프트는 영어, 주석 · 커밋 메시지 · testdata 는 한국어.
제품이 실행 중에 자기 언어를 에이전트에 재주입하는 되먹임을 끊는다는 근거가
같은 문서에 적혀 있다
두 규약과 실제 코드 사이의 관측된 틈은 code-quality-assessment.md 에 적었다.
테스트 배치
전용 테스트 디렉터리가 없다. Go 관례대로 각 패키지 안에 *_test.go 로 함께
산다. internal/contract/testdata/ 가 유일한 픽스처 디렉터리다.