문서와 심볼

aidlc-docs 를 그대로 읽는다 · 읽기 전용
/home/sunny/enode-fixup-workshop2-visualizer연결 중
inception/reverse-engineering/component-inventory.md311 줄

enode — 컴포넌트 인벤토리

15개 패키지 전수. 각 항목의 책임 · 의존 · 주요 파일 · 계측을 적는다.
컴포넌트 이름은 Go 패키지 경로를 그대로 쓴다.

목록 요약

컴포넌트 종류 비테스트 파일 LOC 교차 커버리지
cmd/mediator 실행파일 2 405 0.0%
cmd/enode 실행파일 3 269 0.0%
cmd/enodectl 실행파일 4 531 0.0%
cmd/runctl 실행파일 1 423 0.0%
cmd/iapadapter 실행파일 7 2,436 19.1%
internal/api 라이브러리 1 4,172 65.2%
internal/store 라이브러리 12 4,787 81.0%
internal/contract 라이브러리 5 3,565 88.5%
internal/enode 라이브러리 25 8,254 67.5%
internal/match 라이브러리 1 408 97.5%
internal/record 라이브러리 1 676 81.5%
internal/schema 라이브러리 1 339 75.7%
internal/config 라이브러리 2 481 55.9%
internal/runctl 라이브러리 1 345 37.0%
internal/build 라이브러리 1 120 75.0%

LOC 는 테스트 포함. 커버리지는 -coverpkg=./... 교차 측정치이고 근거는
code-quality-assessment.md 에 있다.

실행파일

cmd/mediator

책임 — 중앙 서버 프로세스. 설정 로드 → 토큰 부트스트랩 → store.Open →
Migrate → reaper 고루틴 기동 → http.Server 기동 → graceful shutdown.

의존 — internal/api · internal/store · internal/record ·
internal/config · internal/build

파일 — main.go(57 statements) · setup.go(154 statements, runSetup 185줄)

주목 — WriteTimeout 을 일부러 걸지 않는다(main.go:114-117). claim 롱폴이
최대 7,200초 대기하므로 쓰기 타임아웃이 있으면 롱폴이 잘린다. 저장소에서
http.Server 를 만드는 유일한 자리(main.go:112)다.

cmd/enode

책임 — 자원이 있는 기계에 상주하는 노드 프로세스. 신원 유도 → 로컬 잠금 →
두 고루틴(광고 루프 + claim 워커) 기동.

의존 — internal/enode · internal/build

파일 — main.go(80 statements, main 173줄) · hook.go(22 statements) ·
setup.go

주목

  • 기동마다 새 instance 를 만든다(main.go:120-128). 이것이 ADR-029/ADR-030
    의 "claim 응답 유실 → 영구 CLAIMED" 위험을 닫는 절반이다
  • 롱폴 클라이언트는 타임아웃이 없는 &http.Client{} 다(main.go:132-136)
  • 하위명령 hook stop 이 곧 R6 의 종료 훅이다. 어떤 실패에도 종료코드 0
    (hook.go:53)
  • --once · --ready-file 이 탄력 노드(단발 기동)용

cmd/enodectl

책임 — 한 기계의 enode 인스턴스 관리. setup · list · id · start ·
stop · logs · status · --version. HTTP 를 부르지 않는다.

의존 — internal/enode · internal/build

파일 — main.go(194 statements) · setup.go · proc_unix.go ·
proc_windows.go

주목 — 셸 스크립트판이 node_id 를 다시 계산하던 것을 없애려고 Go 로
옮겼다. 신원 유도 로직이 한 곳(internal/enode)에만 있게 된다.

cmd/runctl

책임 — 사용자 CLI. 무상태. 8개 하위명령과 4단 종료코드(0~3).

의존 — internal/runctl · internal/build

파일 — main.go(224 statements, run 250줄, usage :402)

주목 — permute() 가 플래그를 앞으로 모아 runctl submit x.json --wait
순서를 받는다. 종료코드가 "Run 실패(1)" 와 "요청 거절(2)" 와 "도달 불가(3)" 를
가르므로 스크립트가 세 상황을 구분할 수 있다.

cmd/iapadapter

책임 — It's a Plan 이슈 트래커 어댑터(ADR-040). 이슈를 읽어 Run 계약을
만들고, 진행과 결과를 이슈 코멘트로 되돌려 놓는다. 바깥을 아는 유일한 부품.

의존 — 내부 패키지 의존 0. 자기 안에서 HTTP 로 Mediator 와 말한다.

파일 — main.go · comment.go · config.go · contract.go ·
itsaplan.go · mediator.go · orchestrator.go

주목

  • 어댑터를 갈아 끼워도 나머지가 안 흔들리도록 의존을 끊어 두었다
  • Mediator 라우트 7개를 부른다. GET /v1/runs/{id}/ledger 를 부르는 유일한
    클라이언트다(mediator.go:271)
  • 이 부품이 CLAUDE.md §2.3 의 「밖으로 나가느냐」 기준의 경계선에 서 있다 —
    읽는 쪽이 하네스가 아니라 사람이다. 관측된 규약 틈은
    code-quality-assessment.md 참조

라이브러리 — 중앙

internal/api

책임 — Mediator 의 HTTP 표면. 15개 라우트, 인증, 오류 코드 체계, 롱폴,
blob 스트리밍.

의존 — internal/config · internal/contract · internal/match ·
internal/record · internal/schema · internal/store

파일 — api.go 단일 파일 830줄. 테스트는 api_test.go 3,342줄에 78개.

주목 — 라우터 라이브러리 의존 0. 라우트 등록이 Handler()(:53) 한 함수에
전부 있다. 15개 전부 s.auth(...) 로 감싸이고 예외 경로가 없다.

internal/store

책임 — PostgreSQL 상태 저장소. Run · Step · Node · Lease · Ask 의 생명주기
전부. 매칭 로직은 여기 없다.

의존 — internal/contract · internal/match · internal/record

파일 — 동사별로 12개로 갈려 있다.

파일 맡는 것 파일별 커버리지
store.go Open · Migrate · CreateRun · Capabilities 80.5%
claim.go (938줄) RenewLeases · ClaimStep · ReportStep · redeliver · FailRestarted 84.5%
verdict.go 상태 상수 · Check · Verdict · Verify 97.5%
reap.go Reap · sealExpired · RunReaper · Cancel · SettleIfDone 66.7%
expand.go applyExpands(273줄) · maxContractVersions 80.0%
dispatch.go applyDispatch · propagateSkips · readDispatchValue 77.0%
acquire.go runAcquires · doAcquire · tryGrab 76.0%
release.go applyRelease 86.2%
rollback.go rollBack · loopBack · validateBack · untilMet 82.0%
seal.go sealRecord · ContractVersion · Manifest 84.7%
ask.go raiseAsks · PushAsks · AnswerStep · ExpireAsks 79.6%
observe.go Steps · Ledger · priorRuns · entriesOf 78.9%

schema.sql(144줄)이 //go:embed 로 이 패키지에 들어간다.

주목 — 90개 함수 전부가 테스트에 한 번 이상 들어간다. 자기 패키지 기준
커버리지(5.5%)와 교차 커버리지(81.0%)의 차이가 큰 이유는
code-quality-assessment.md 첫 절에 있다.

internal/contract

책임 — 계약 · 광고 타입과 그 검증. 넷의 공통 어휘. Mediator 와 노드가
공유하는 유일한 타입 정의다.

의존 — 없음(다른 내부 패키지를 부르지 않는다)

파일 — contract.go(1,724줄) · grammar.go · checkplan.go ·
planshape.go · advert.go

주목 — Contract.Validate()(contract.go:954)가 612줄이다. 계약 문법
전체의 단일 검증 지점이며, 역할 · 간선 · DAG · 스키마 경계 · 단계 종류 · 예약
이름 · dispatch 목적지 · ask 형태를 전부 여기서 본다. expands 로 자란 계약의
재검증 지점이기도 하다(expand.go:212 · :224).

internal/match

책임 — 요구 → 노드. 순수 함수. 입력 셋 · 출력 둘 · I/O 0.

의존 — internal/contract

파일 — match.go 단일

주목 — POST /v1/runs 와 POST /v1/runs/dry-run 이 같은 함수를 부른다.
거절 코드 둘(CodeNoCandidate = 422 영구 · CodeAllBusy = 409 일시)이 여기서
난다. attrCount(:34)가 속성이 적은 노드를 먼저 주는 정렬을 만든다
(ADR-027). 이 저장소에서 가장 잘 덮인 컴포넌트(97.5%)다.

internal/record

책임 — Run Record. DB 가 아니라 파일시스템. 봉인 · blob · tar.

의존 — 없음

파일 — record.go 단일

주목 — 봉인은 파일 0444 · 디렉터리 0555 로 건다(:156 seal, 순서 이유가
주석에 있다). blob 의 「최신」은 (회차, 순번)이 가장 큰 것이고 회차가 파일
이름에 들어간다(01.1-name 꼴, :260 blobPath · :294 parseBlobName).
:193 unseal 의 주석이 운영 함의를 스스로 적는다 — "봉인은 삭제까지 막는다.
… 디스크가 차면 사람이 지우지도 못한다."

internal/schema

책임 — 형식 전용 JSON Schema 검증기.

의존 — 없음

파일 — schema.go 단일

주목 — CheckBoundary(:46)가 minimum · minLength 같은 품질 판정
키워드를 계약 단계에서 거절
한다. 스키마가 제약할 수 있는 것은 형식뿐이고
판정은 success_when 이 한다는 경계를 코드로 세운 자리다.

internal/config

책임 — Mediator 설정(YAML) 로드와 기본값.

의존 — 없음

파일 — config.go 외 1

주목 — 사용자 경로가 /etc 를 이긴다(ADR-015 §4 의 우선순위).
:57 이 Lease 값 셋(ttl_seconds 3600 · renew_seconds 60 ·
not_after_factor 3)에 대해 "지금은 임시치이며 S4 에서 실측으로 고친다" 고
적는다. :29 가 Artifacts.Root 와 MaxBlobBytes(기본 10 MiB)를 든다.

라이브러리 — 노드

internal/enode

책임 — 노드 런타임 전부. 신원 · 잠금 · 탐지 · 광고 · claim 워커 · 하네스
어댑터 · 환경 화이트리스트 · 워크스페이스 · 산출물 수거 · 종료 훅.

의존 — internal/contract 하나뿐. internal/store 도 internal/api 도
import 하지 않는다 — 노드가 Mediator 내부를 못 보는 것이 컴파일러가 지키는
사실이다.

파일 — 25개 비테스트 · 20개 테스트(테스트 함수 137개). 이 저장소에서 파일이
가장 많은 패키지.

묶음 파일
신원 · 잠금 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

주목

  • runner.go:54 runHarness 가 저장소의 유일한 exec 지점이다(R3)
  • env.go:97 harnessEnv 는 블랙리스트가 아니라 빈 것에서 쌓아 올리는
    화이트리스트
    다. ENODE_TOKEN 이 하네스로 넘어가지 않는다(R1)
  • claim.go(873줄)가 이 패키지에서 압도적으로 가장 안 덮인 파일이다(24.8%).
    같은 패키지 다른 파일은 대부분 75~96%. execute(222줄) · runAgentStep(123줄)
    · uploadProduced · report 재시도 루프 · 임대 워치독 고루틴(:498-514)이
    여기 있고, 이것이 S4 위험의 노드 쪽 절반이다
  • detect.go:20 Detect 가 광고하는 속성은 os · host_arch · harness ·
    ws · repo · arch · board · tag + 사람이 붙인 라벨이다.
    R7 이 규정한 sandbox 키가 없다(관측; architecture.md 관측 2)

internal/runctl

책임 — runctl 의 HTTP 클라이언트. 12개 메서드로 8개 라우트를 부른다.

의존 — 없음

파일 — client.go 단일

주목 — 12개 메서드 중 테스트가 들어가는 것이 4개(37.0%)다.

internal/build

책임 — <cmd> --version 한 줄. 링커가 박거나 Go VCS 스탬프에서 읽는다.

의존 — 없음

파일 — build.go 단일

주목 — 다섯 실행파일 전부가 이것 하나를 공유한다. 버전 문자열이 두 곳에
적히지 않는다.

컴포넌트 간 의존 요약

   cmd/mediator    → api, store, record, config, build
   cmd/enode       → enode, build
   cmd/enodectl    → enode, build
   cmd/runctl      → runctl, build
   cmd/iapadapter  → (내부 의존 없음)

   internal/api    → config, contract, match, record, schema, store
   internal/store  → contract, match, record
   internal/enode  → contract
   internal/match  → contract
   internal/{contract, record, schema, config, build, runctl} → (없음)

순환 0건. 방향은 언제나 바깥(표면 · 실행파일)에서 안(어휘 · 상태)으로만 흐른다.

소유권 — 어느 컴포넌트가 무엇을 소유하는가

개념 소유 컴포넌트 근거
계약 · 광고 타입 정의 internal/contract 넷이 공유하는 유일한 타입
배정 결정 internal/match 순수 함수, store 도 api 도 결정하지 않는다
Run · Step · Lease 상태 internal/store 상태 상수와 전이가 전부 여기
봉인된 기록 internal/record 파일 권한이 불변성의 기계
HTTP 표면과 오류 코드 internal/api 단일 ServeMux
노드 신원 internal/enode hash(email · hostname · realpath(config))[:12]
하네스 실행 internal/enode/runner.go 유일한 exec 지점
바깥 트래커 cmd/iapadapter 유일하게 바깥을 안다

애매한 소유권이 관측되지 않았다 — 각 개념이 정확히 한 컴포넌트에 산다.