문서와 심볼

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

enode — 코드 품질 평가

이 문서의 첫 절이 이번 스캔의 가장 큰 발견이다. 워크플로가 들고 있던
커버리지 기준선이 틀렸고, 그것이 「테스트 공백이 어디인가」의 답을 통째로
바꾼다. 뒤의 어떤 절보다 먼저 읽어야 한다.

계측치 정정 — 이전 커버리지 수치가 왜 틀렸나

무슨 일이 있었나

이 워크플로가 들고 있던 패키지별 커버리지 수치는 go test ./... -cover 를
ENODE_TEST_DATABASE_URL 없이 돌려 얻은 것이다.

internal/api/api_test.go:33 과 :339 가 그 환경변수가 없으면 스위트 전체를
t.Skip 한다.

t.Skip("ENODE_TEST_DATABASE_URL is unset — see scripts/testdb.sh")

그래서 이전 수치는 78개 통합 테스트가 통째로 안 돈 상태의 것이었고,
internal/api 가 0.0% 로 보였다.

왜 미래의 독자가 같은 실수를 반복하는가

이 저장소에서 go test ./... -cover 를 아무 준비 없이 돌리면 아무 오류 없이
통과한다.
78개가 조용히 사라지는데 종료코드는 0 이다. 그래서 수치가 틀렸다는
신호가 어디에도 뜨지 않는다. 다시 재는 사람은 반드시 두 가지를 해야 한다.

  1. scripts/testdb.sh 로 Postgres 17 을 띄우고 ENODE_TEST_DATABASE_URL 을 넣는다
  2. -coverpkg=./... 로 교차 커버리지를 잰다 (아래 참조)

CI 는 이 함정을 알고 있어서 internal/api 에 SKIP 이 한 줄이라도 나오면
실패시키는 전용 단계를 따로 둔다(ci.yml 의 "DB 테스트가 스킵되지 않았는지").
로컬에는 그 방어가 없다.

internal/store 의 5.5% 가 왜 거짓인가

internal/store 는 자기 패키지 기준으로 5.5% 로 나온다. 그 이유는
internal/store 의 테스트 파일 둘(verdict_test.go · seal_manifest_test.go)이
DB 를 안 쓰는 순수 단위 테스트이기 때문이다.

store 를 실제로 도는 것은 internal/api 의 78개 통합 테스트이고, Go 의
패키지별 커버리지는 그 교차 실행을 계산에 넣지 않는다.

-coverpkg=./... 로 다시 재면 internal/store 는 81.0% 이고,
90개 함수 전부가 테스트에 한 번 이상 들어간다.

커버리지 — 교차 측정 (-coverpkg=./..., DB 붙임)

   97.5%   internal/match
   88.5%   internal/contract
   81.5%   internal/record
   81.0%   internal/store        (패키지별 측정의 5.5% 가 아니다)
   75.7%   internal/schema
   75.0%   internal/build
   67.5%   internal/enode
   65.2%   internal/api          (DB 없이 재면 0.0%)
   55.9%   internal/config
   37.0%   internal/runctl
   19.1%   cmd/iapadapter
    0.0%   cmd/enode · cmd/enodectl · cmd/mediator · cmd/runctl
   ─────
   58.6%   전체 (3,485 / 5,951 statements)

internal/store 파일별 (교차, 문장 기준)

   97.5%  verdict.go     86.2%  release.go     84.7%  seal.go
   84.5%  claim.go       82.0%  rollback.go    80.5%  store.go
   80.0%  expand.go      79.6%  ask.go         78.9%  observe.go
   77.0%  dispatch.go    76.0%  acquire.go     66.7%  reap.go

reap.go 66.7% 가 store 내부 최저이고, 안 덮인 부분은 주로 sealExpired 의
오류 분기다.

실제 테스트 공백 — 우선순위

정정된 계측이 「가장 안 덮인 자리」의 위치를 바꾼다. internal/store 가
아니다.

1. cmd/ 네 실행파일 — 0%, 682 statements

파일 statements 안 덮이는 것
cmd/runctl/main.go 224 하위명령 디스패치 · 플래그 permute · 4단 종료코드 판정
cmd/enodectl/main.go 194 프로세스 관리(start · stop · logs · status)
cmd/mediator/setup.go 154 runSetup(185줄) — 초기 설정 생성
cmd/enode/main.go 80 이중 고루틴 수명 관리 · --once 종료 판정(:170-190) · instance 생성(:120-128)
cmd/mediator/main.go 57 부팅 순서 · 설정 해석 · 토큰 부트스트랩 · reaper 기동 · graceful shutdown
cmd/enode/hook.go 22 종료 훅 진입점(어떤 실패에도 종료코드 0)

부팅 순서 · 설정 해석 · 토큰 부트스트랩 · reaper 기동 · graceful shutdown ·
이중 고루틴 수명 · --once 종료 · 프로세스 관리가 전부 여기 있고 전부
0%
다.

2. internal/enode/claim.go — 24.8% (322 중 80)

S4 위험의 노드 쪽 절반이 여기다. 873줄짜리 워커 실행 경로이고,
internal/enode 패키지 안에서 압도적으로 가장 안 덮인 파일이다 — 같은 패키지의
다른 파일은 대부분 75~96%.

이 파일이 들고 있는 것:

   execute            (claim.go:383, 222줄)
   runAgentStep       (123줄)
   uploadProduced     (:728)
   report 재시도 루프  (:309, 닿을 때까지 재시도)
   임대 워치독 고루틴   (:498-514)

execute 하나가 임대 워치독 고루틴 · $IN 적재와 sealInput · argv 확장 ·
명령 단계 실행 · 로그 업로드 순서를 전부 들고 있다.

README.md:34 가 S4 를 최대 위험으로 지목한 것과 계측이 같은 곳을 가리킨다.

3. cmd/iapadapter — 19.1%

   itsaplan.go      0.0%
   main.go         10.1%
   orchestrator.go 14.5%

바깥 트래커와 말하는 경로가 거의 안 덮여 있다.

4. internal/enode 의 나머지 저커버 파일

   paths.go      48.6%
   leases.go     52.4%      (임대 보유 상태 — S4 와 인접)
   diff.go       58.9%
   workspace.go  61.4%
   setup.go      64.7%

5. internal/runctl/client.go — 37.0%

12개 메서드 중 4개만 테스트에 들어간다.

테스트 구성

배치

전용 테스트 디렉터리가 없다. Go 관례대로 각 패키지 안에 *_test.go 로 함께
산다. 33개 파일에 306개 테스트 함수, 11개 패키지에 분포한다.

패키지 테스트 함수 파일
internal/enode 137 20
internal/api 78 1 (3,342줄)
internal/contract 32 3
internal/store 13 2
cmd/iapadapter 13 1
internal/record 10 1
internal/match 9 1
internal/config 5 1
internal/runctl 4 1
internal/schema 3 1
internal/build 2 1
합계 306 33

cmd/enode · cmd/enodectl · cmd/mediator · cmd/runctl 넷은
[no test files] 다.

없는 테스트 종류

벤치마크 · 퍼즈 · Example 테스트가 0건이다 (계측 확인:
grep -rhoE '^func (Benchmark|Fuzz|Example)' --include='*_test.go' → 0).

internal/schema 의 JSON Schema 파서와 internal/contract 의
Validate()(612줄)가 퍼즈의 자연스러운 대상인데 하나도 없다.

프레임워크

표준 testing 만. assertion 라이브러리도 mock 라이브러리도 없다.
internal/api/api_test.go 는 mock 대신 실제 PostgreSQL 을 띄워 httptest 로
서버를 세우는 통합 스위트다.

커버리지 설정

부재. 커버리지 임계선을 강제하는 설정이 없고, CI 에 커버리지를 재는 단계도
없다.
위의 수치는 전부 이번 스캔에서 손으로 잰 것이다.

실행 시간

  • internal/enode — 약 29초 (전체 스위트의 사실상 전부)
  • internal/api — DB 가 붙으면 약 4.9초

스킵 조건 전수 (t.Skip 호출 16곳)

조건 곳 위치
심볼릭 링크 생성 불가 3 internal/enode/collect_test.go:77,101,121
sh 부재 3 internal/enode/env_test.go:106,139,184
ENODE_TEST_DATABASE_URL 부재 2 internal/api/api_test.go:33,339
packaging 예시 부재 2 internal/enode/examples_test.go:19,64
root 로 실행 1 internal/enode/collect_test.go:223
claude 미설치 1 internal/enode/claude_test.go:111
하네스도 저장소도 없음 1 internal/enode/enode_test.go:152
홈 디렉터리 부재 1 internal/enode/paths_test.go:34
git 사용 불가 1 internal/enode/workspace_test.go:157
자식 프로세스 스폰 불가 1 cmd/iapadapter/adapter_test.go:272

가장 위험한 것은 ENODE_TEST_DATABASE_URL 두 곳이다 — 그 둘이 78개를
조용히 없앤다. CI 만이 그것을 잡는다.

정적 검사와 CI/CD

강제되는 검사

golangci-lint 설정이 없다. 강제되는 것은 셋이다.

  1. gofmt -l . 가 비어 있어야 통과
  2. go vet ./...
  3. 저장소 고유 검사 — .github/workflows/ci.yml 의 「장식 문자가 없는지」
    잡이 grep -rlIP '\x{2605}' 로 저장소 전체에서 U+2605 를 찾아 하나라도 있으면
    실패시킨다. 현재 0건

//nolint: 지시자는 28곳이고 전부 errcheck 이며, 거의 다
defer f.Close() · defer tx.Rollback(ctx) 형태다(여럿에 이유 주석이 붙어 있다).

워크플로 3개

ci.yml — push(main) · pull_request 트리거. 잡 둘.

test 잡은 Postgres 17 서비스 컨테이너를 띄우고 ENODE_TEST_DATABASE_URL 을
넣은 뒤 여섯 단계를 돈다.

   ① go version
   ② 장식 문자가 없는지        (U+2605 검사)
   ③ gofmt -l .
   ④ go vet ./...
   ⑤ go test ./... -count=1
   ⑥ DB 테스트가 스킵되지 않았는지  (internal/api 에 SKIP 이 없는지 확인)

⑥ 이 이 저장소의 가장 좋은 CI 장치다 — DB 테스트가 조용히 사라지는 것을 막는
전용 단계이고, 이번 스캔이 정정한 바로 그 함정을 겨눈다.

cross 잡은 GOOS=windows/amd64 · linux/arm GOARM=7 · darwin/arm64 로
go build ./... 를 돌린다. Go 버전은 go-version-file: go.mod 로 한 곳에서만
온다.

package.yml — 172줄. packaging-experiment 브랜치 push ·
workflow_dispatch · workflow_call 셋으로만 돈다(커밋마다 돌지 않는다).
잡 셋: build(deb · rpm · msi 를 리눅스에서 굽는다, msi 는 wixl) ·
verify-linux(실제로 설치해 본다) · verify-windows.

release.yml — v* 태그 push. 자기 빌드를 짓지 않고 package.yml 을
workflow_call 로 그대로 부른다. 검증 잡이 전부 초록일 때만 publish 가 돈다.

CI 에 없는 것

  • 커버리지 측정 단계 없음
  • 커버리지 임계선 없음
  • golangci-lint 없음
  • 보안 스캐너(govulncheck · gosec) 없음
  • 의존 취약점 검사 없음

문서화 품질

강점 — doc comment 가 「왜」를 적는다

이 저장소에서 가장 두드러지는 성질이다. 거의 모든 패키지 · 타입 · 함수에
doc comment 가 붙어 있고, 그 내용이 「무엇을 한다」가 아니라 왜 이 형태인가 ·
어떤 대안을 왜 기각했나 · 어떤 실측이 이것을 고치게 했나
다.

실측 사례가 식별자와 함께 인용되는 자리가 20곳 이상이다 — vm-scratch-7 ·
zephyr-setup-8 · colima-enode-1 · third-run-1 · reject-3 · rewind-1.

internal/store/schema.sql(144줄)은 스키마 파일이라기보다 주석이 절반 이상인
설계 문서에 가깝다 — 각 컬럼이 왜 있는지, CHECK 을 왜 안 거는지가 적혀 있다.

저장소 문서

  • README.md 21,955바이트(483줄) — 슬라이스 표(S1~S10) · 패키지 배치 ·
    불변식이 어디서 강제되는지를 코드 인용과 함께 적는다
  • CLAUDE.md 2,768바이트(72줄) — 두 개의 저장소 고유 규약(아래)

ADR 역추적성 — 이 저장소의 두드러진 강점

ADR-\d{3} 이 Go 소스에서 55개 서로 다른 번호로 인용된다(계측 확인).
ADR-INDEX.md 가 아는 62개 중 저장소 어디에도 안 나오는 것은 7개뿐이다.

안 나오는 ADR 무엇 성격
ADR-001 Broker 안 둔다 부재의 결정 — 인용할 코드가 없다
ADR-003 점유는 Mediator 소유 내용은 구현돼 있고 번호만 안 붙었다 — internal/store/schema.sql 의 leases 주석과 internal/store/store.go
ADR-006 테스트 두 종류
ADR-034 하네스 팩 system.md §5 가 「설계만」으로 분류한 것과 일치
ADR-035 부칠 수 없는 것만 요구 system.md §5 가 「설계만」으로 분류한 것과 일치
ADR-039 완주만 판정 내용은 구현돼 있고 번호만 안 붙었다 — internal/store/claim.go:737-748 과 internal/api/api.go:294-297 이 ADR-004 · I3 를 인용한다
ADR-041 워크스페이스가 세 가지를 겸한다

없는 문서

OpenAPI / Swagger 없음(정본이 "OpenAPI 로 굳히지 않는다" 를 명시). godoc 외의
생성 문서 없음.

저장소 고유 규약과 그 집행 사이의 틈

CLAUDE.md 가 두 규약을 세운다. 둘 다 실제 코드와 틈이 관측된다. 판정이
아니라 관측으로 적는다.

규약 1 — 장식 문자를 안 쓴다

CLAUDE.md §1.2 는 「주석 · 문자열 · 로그 · 테스트 메시지 · 프롬프트 · 설정
파일 어디에도」 장식 문자를 안 넣는다고 적는다.

CI 가 집행하는 것은 U+2605 하나뿐이다 (현재 0건).

계측(이번 스캔에서 직접 재었다): U+2717 이 14줄, U+2713 이 2줄, 합 16줄.
그중 주석이 9줄이고, 주석이 아니라 실제 출력 문자열인 것이 7줄이다.

주석 9줄 — 「기각한 대안」을 표시하는 관행에 쓰인다. 규약 문면상으로는 이것도
대상이다.

   internal/enode/changed.go:24, 26, 28
   internal/enode/diff.go:21
   internal/enode/hook.go:21
   internal/enode/workspace.go:109
   internal/schema/schema.go:9, 10
   cmd/enode/hook.go:14

실제 출력 문자열 7줄 — 사람이 읽는 곳으로 나간다.

   cmd/enodectl/main.go:71       fmt.Fprintf(os.Stderr, "✗ %v\n", err)
   cmd/enodectl/main.go:258      fmt.Fprintln(os.Stderr, "✗ did not come up. tail of the log:")
   cmd/runctl/main.go:388        mark = "✗ "        (fmt.Printf 로 나간다)
   cmd/iapadapter/comment.go:103 mark  = "✗"        (이슈 코멘트 판정 표시)
   cmd/iapadapter/comment.go:105 mark  = "✓"
   cmd/iapadapter/comment.go:137 sch   = "✓"
   cmd/iapadapter/comment.go:139 sch   = "✗"

규약 2 — 밖으로 나가는 것은 영어

CLAUDE.md §2 는 밖으로 나가는 문자열(에러 · 로그 · CLI 출력 · 테스트 이름 ·
픽스처 · 에이전트 프롬프트)을 영어로 쓴다고 적는다. 기준을 「코드냐」가 아니라
**「밖으로 나가느냐」**로 세운 이유까지 적혀 있다 — 제품이 실행 중에 자기 언어를
에이전트에 재주입하는 되먹임을 끊는다.

계측: cmd/iapadapter 는 It's a Plan 이슈 코멘트와 result 본문으로 나가는
문자열 20곳 이상이 한국어다.

   main.go:156,163,208,222,238,245,250,332,358,385,396,450,458,459,467
   comment.go:23,28,36,43-46,55,107

예: comment.go:107 의
"\n\n…(잘렸습니다. 전문은 봉인된 Record 에 있습니다)".

이 부품이 §2.3 이 말한 「밖으로 나가느냐」 기준의 경계선에 서 있다 — 읽는
쪽이 하네스가 아니라 사람이다. 규약이 이 경우를 어떻게 판정하는지는 이 문서가
결정하지 않는다.

기술 부채 — 통상적 신호는 0건

계측 확인. 흔히 찾는 신호가 하나도 없다.

신호 계측
TODO · FIXME · XXX · HACK · WIP 주석 0건 (전 Go 파일)
비테스트 코드의 panic( 0건
하드코딩된 자격증명 · API 키 0건 (토큰은 설정 파일 · 환경변수 경유이고, internal/enode/env.go 화이트리스트가 ENODE_TOKEN 을 하네스로 안 넘긴다)
순환 의존 0건
미사용 의존 0건 (직접 3개 모두 실사용)
gofmt -l . 무출력
go vet ./... 무출력
go test ./... -count=1 DB 유무 양쪽 모두 전 패키지 ok. 실패 · 플레이크 0건

TODO 를 안 쓰는 대신 「자리만 파 둔 것」을 doc comment 로 표시하는 관행이 있다
(internal/store/seal.go:95 ByMediator · internal/enode/env.go:60
RunIdentity · :88 Transparent{}).

기술 부채 — 실제로 있는 것 (구조적)

1. 계측 공백이 위험한 자리와 겹친다

위 「실제 테스트 공백」 참조. 요약: cmd/ 넷이 0% 이고
internal/enode/claim.go 가 24.8% 인데, 그 둘이 부팅 · 수명 · 임대 · 실행이라는
가장 되돌리기 어려운 경로다.

2. 함수 크기

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

30줄 넘는 함수 상위: applyExpands(273) · cmd/runctl/main.go run(250) ·
Worker.execute(222) · buildPrompt(210) · Verify(194) ·
cmd/mediator/setup.go runSetup(185) · RenewLeases(182) ·
cmd/enode/main.go main(173).

파일 크기: internal/contract/contract.go 1,724줄 · internal/store/claim.go
938줄 · internal/enode/claim.go 873줄 · internal/api/api.go 830줄. 테스트
파일 internal/api/api_test.go 는 3,342줄 단일 파일에 78개 테스트.

3. 중복

무엇 어디 성격
func nonce() string internal/api/api.go:826 · internal/store/acquire.go:243 본문이 완전히 동일(rand.Read(16) → hex.EncodeToString). 둘 다 ADR-010 허가 아티팩트의 nonce 를 만든다
func itoa(int) string internal/enode/harness.go:145 · internal/store/seal.go:228 store 쪽은 음수 처리가 없다(양수 seq 전용)
byStep / ByStep internal/store/expand.go:350(비공개) · internal/store/seal.go:100(공개) 같은 패키지 안에서 같은 본문("step:" + id)을 두 벌로 갖는다. seal.go:98 주석은 "아직 부르는 곳이 없다" 라고 적지만 seal_manifest_test.go:67-68 이 부르고, 실사용 호출 6곳은 전부 소문자 쪽이다

4. 마이그레이션 · 운영

  • Store.Migrate 가 schema.sql 전문을 매 기동마다 Exec 한다. 버전
    테이블도 롤백도 없다(store.go:71 이 의도를 적는다). schema.sql 이 이미
    ALTER TABLE ... ADD COLUMN IF NOT EXISTS 를 9번 쌓아 올린 상태이고,
    그중 하나(steps.needs)는 백필 UPDATE + SET NOT NULL 을 매 기동 다시
    돈다
  • 보관 · 정리 정책이 없다. internal/record/record.go:193 unseal 의 주석이
    운영 함의를 스스로 적는다 — "봉인은 삭제까지 막는다. … 디스크가 차면 사람이
    지우지도 못한다." 이 함수는 지금 테스트 정리에만 쓰인다
  • sealExpired 의 한 시간 창. internal/store/reap.go:106 의 조회가
    ended_at > now() - interval '1 hour' 를 쓴다. Mediator 가 한 시간 넘게 죽어
    있으면 그 사이 종료된 Run 은 이 경로로 봉인되지 않는다
  • 임시치가 셋 있다. internal/config/config.go:57 이 Lease 값 셋
    (ttl_seconds 3600 · renew_seconds 60 · not_after_factor 3)에 대해
    "지금은 임시치이며 S4 에서 실측으로 고친다" 고 적는다
  • claim 롱폴이 1초 티커 폴링이다. internal/api/api.go:225 주석: "MVP 는
    짧은 주기로 DB 를 다시 본다. ADR-015 가 적어둔 LISTEN/NOTIFY 는 …
    최적화다. 지금은 넷이다." (:231, 최대 7,200초)
  • blob 을 직접 서빙한다. internal/api/api.go:759 getBlob — "ADR-018 —
    나중에 여기서 302 로 저장소를 가리킨다. 지금은 직접 서빙한다"

5. 트랜잭션 경계를 넘는 부수효과

  • internal/store/claim.go:339-343 — ClaimStep 이 tx.Commit 뒤에
    stampStanding · stampRejections · stampRoleAttrs · stampLedger 넷을
    부른다. 각 함수가 실패를 삼키고 조용히 돌아온다(주석이 의도라고 적는다 —
    "실패해도 단계를 막지 않는다"). redeliver(claim.go:544-547)도 같은 넷을
    부르는데, 두 경로가 같은 프롬프트를 만들어야 한다는 요구를 코드 중복으로
    지킨다
  • internal/store/acquire.go doAcquire 는 s.Records.WriteBlob 을 트랜잭션
    안에서 부른다 — 파일시스템 쓰기가 롤백되지 않는다
  • internal/store/store.go:299 — CreateRun 이 tx.Commit 전에
    s.Records.Open(runID) 를 부른다(주석이 순서 이유를 적는다). 커밋이 실패하면
    디렉터리가 남는다
  • internal/store/ask.go:102 PushAsks 가 go func() 로 웹훅을 쏘고 결과를
    기다리지 않는다 — 프로세스가 즉시 죽으면 유실된다(설계상 허용: 인박스가 정본)

6. 무시된 오류

비테스트 코드에서 _ = 로 오류를 버리는 지점 39곳. 대부분
defer Close() / Rollback 이지만, 판단이 섞인 것 넷이 있다.

위치 무엇 함의
internal/api/api.go:770 io.Copy blob 스트리밍 중 실패가 로그에도 안 남는다
internal/api/api.go:828 rand.Read 실패하면 nonce 가 전부 0바이트가 된다
internal/store/acquire.go:245 rand.Read 위와 동일
cmd/iapadapter/main.go:395,457,466 이슈 코멘트 전송 실패를 버린다

또 internal/store/reap.go:81 은 sealExpired 오류를 로그만 남기고 Reap 은
성공으로 돌아온다.

7. 죽은 코드 · 자리만 판 것

  • internal/store/seal.go:95 ByMediator = "mediator" — 주석이 "아직 안 쓴다"
    라고 적고 실제 참조 0건
  • internal/enode/env.go:60 RunIdentity — "지금은 아무도 안 본다"
    (R2 요청자 신원용 자리)
  • internal/enode/env.go:88 Transparent{} — 아무것도 주입 안 하는
    Credentials 구현. 의도적으로 nil 대신 타입을 둔 것을 주석이 적는다
  • internal/store/claim.go:836 _ = i (SettleIfDone 의 재시도 소진 표시
    루프. reap.go:246 에도 같은 형태), internal/store/acquire.go:120
    _ = taken, internal/enode/detect.go:64 _ = ver — 셋 다 이유 주석이
    붙어 있다
  • R7 의 광고 속성 sandbox — Go 코드 전체에서 문자열 sandbox 가 0건.
    Detect() 가 광고하는 것은 os · host_arch · harness · ws · repo ·
    arch · board · tag + 사람이 붙인 라벨이다. 관측이며 판정이 아니다
    (architecture.md 관측 2)

문서 드리프트 — 관측

판정이 아니라 관측이다.

  • README.md §배치 가 나열하는 실행파일은 cmd/mediator · cmd/enode ·
    cmd/runctl 셋("셋이 다 있다")이다. 저장소에는 cmd/enodectl 과
    cmd/iapadapter 가 더 있다
  • README.md §배치 에서 internal/enode 가 두 줄에 걸쳐 두 번 나온다
  • system.md §5 는 "runctl asks / answer · show 로 질문의 내용을 함께
    보여주고" 라고 적는다. cmd/runctl 의 하위명령 목록(main.go:124-310 ·
    usage() :402)에 show 가 없다. ledger 하위명령도 없다
  • 표면 개수를 말하는 문장 셋이 서로 다른 수를 든다(12 / 12→13 / 13) —
    api-documentation.md 마지막 절 참조

이번 스캔이 환경에 남긴 것

scripts/testdb.sh 로 도커 컨테이너 enode-test-pg 를 만들어 커버리지를
재측정한 뒤 docker stop 했다. 컨테이너는 중지 상태로 남아 있고
docker rm enode-test-pg 로 완전히 지울 수 있다. 저장소 파일은 하나도 고치지
않았다.