문서와 심볼

aidlc-docs 를 그대로 읽는다 · 읽기 전용
/home/sunny/enode-fixup-workshop2-visualizer연결 중
construction/u-foundation/code/code-generation-summary.md179 줄

U-바닥 — Code Generation 결과

계획은 aidlc-docs/construction/plans/u-foundation-code-generation-plan.md
이고 체크박스가 전부 [x] 다. 이 문서는 그 실행의 결과다.

밖에서 보이는 변화가 없다. 라우트를 하나도 안 만들었고 기존 15개가
그대로 돈다. 이 유닛이 낸 것은 화면도 응답도 아니라 나머지 넷이 딛는
타입과 열이다.


1. 만든 파일

   internal/store/list.go                  ListNodes · ListRuns · 조회 타입 일곱
   internal/store/queue.go                 StateQueued · DrainingNodes · ListQueued
   internal/store/list_test.go             12개.  DB 필요 열 · DB 불필요 둘
   internal/store/queue_test.go            5개
   internal/store/advert_policy_test.go    4개
   internal/contract/advert_policy_test.go 4개.  DB 불필요
   internal/match/reject_json_test.go      4개.  DB 불필요
   internal/boundary/imports_test.go       2개.  테스트 파일 하나뿐인 패키지

2. 고친 파일

   internal/store/schema.sql     ALTER TABLE nodes ADD COLUMN drain 한 줄
   internal/store/store.go       UpsertAdvert 의 시그니처와 질의 · clampDrain
   internal/store/observe.go     StepView.Chosen · SELECT 목록과 Scan 에 한 자리씩
   internal/contract/advert.go   Policy 타입 · Advert.Policy
   internal/match/match.go       Reject 세 필드에 json 태그
   internal/api/api.go           호출 한 줄 (208).  등록 줄도 핸들러 로직도 아니다

api.go 한 줄이 unit-boundaries.md §2 의 파일 행렬과 어긋난다. 근거는
business-logic-model.md §2.2 이고 aidlc-state.md 의 「설계 중 나온 정정
셋」이 이미 실었다.


3. 설계에서 갈린 것 하나 — omitzero

domain-entities.md §2.2 가 Advert.Policy 의 태그를 omitempty 로
적었다. Go 에서 omitempty 는 구조체 필드에 안 듣는다. 제로값이
"policy":{} 로 나간다.

   실측    omitempty   {"n":"x","policy":{}}
           omitzero    {"n":"x"}
           값이 있을 때 {"n":"x","policy":{"drain":"graceful"}}

같은 문서 §2.2 와 계획 Step 3 이 「제로값이면 policy 키가 빠진다」를
요구하므로, 그 요구를 내는 태그는 omitzero 다 (Go 1.24 부터. 이 저장소는
1.26). 빈 객체가 나가면 안 되는 이유는 R4 다 — 읽는 쪽이 「정책 필드를
아는 노드가 아무것도 안 걸었다」와 「옛 노드라 필드가 없다」를 가르려 들게
되는데, R4 는 그 둘을 같은 것으로 읽으라고 정했다.

테스트가 그것을 못 박는다 — TestAdvert_AZeroPolicyLeavesNoKey.


4. CP0 — 돌린 결과

   go build ./...                     exit 0
   go test ./...                      16개 패키지 전부 ok.  스킵 0건
   go vet ./...                       exit 0
   go run ./scripts/glyphscan.go      83 files scanned, no decorative glyph
   golangci-lint run ./...            20건.  기준선 그대로이고 새 파일에는 0건
                                      (v2.13.2 — ci.yml 이 고정한 버전과 같다)
   커버리지 게이트                     15개 패키지 전부 하한 80% 통과.  미달 0개
   go test -v ./internal/boundary     1 checked, 3 skipped, 4 total
   curl GET /v1/capabilities          200 {"capabilities":[]}
   라우트 15개 회귀                    전부 살아 있다.  mux 404 는 하나도 없다

4.1 커버리지 — internal/store 가 올랐다

             전                후
   internal/store   1213/1498 = 81.0%   1317/1602 = 82.2%
   전체              5460/6247 = 87.4%

문장이 104개 늘고 커버리지가 함께 올랐다. internal/boundary 는 프로파일에
안 나온다 — 문장이 0 이라 하한 검사의 분모를 안 바꾼다 (설계가 예측한 그대로).

4.2 라우트 회귀 — 15개가 살아 있고 셋은 아직 없다

   POST /v1/nodes                          200
   POST /v1/nodes/{id}/claim               롱폴로 매달린다 (라우트 있음)
   POST /v1/runs/{run}/steps/{seq}/result  409
   POST /v1/runs                           400
   POST /v1/runs/dry-run                   400
   GET  /v1/runs/{id}                      404   핸들러의 404 다
   GET  /v1/capabilities                   200
   GET  /v1/asks                           200
   POST /v1/runs/{run}/steps/{seq}/answer  409
   GET  /v1/runs/{id}/ledger               404
   GET  /v1/runs/{id}/record               404
   POST /v1/runs/{id}/cancel               404
   PUT  /v1/runs/{run}/steps/{seq}/log     404
   PUT  /v1/runs/{run}/steps/{seq}/blob/{name}  404
   GET  /v1/runs/{run}/blob/{name}         404

   아직 없어야 하는 셋
   GET  /v1/runs    405    같은 경로에 POST 만 있다.  GET 이 아직 없다
   GET  /v1/nodes   405    같은 이유
   GET  /ui/        404    mux 의 404 다.  U-관측API 가 만든다

핸들러의 404 와 mux 의 404 를 본문으로 갈랐다 — 404 page not found 가
나오는 것만 라우트 없음으로 셌다.

4.3 정책 왕복을 살아 있는 Mediator 로 확인했다

   policy: graceful 을 실은 광고     200.  nodes.drain = graceful
   policy: gracefull (오타)          200.  nodes.drain = graceful 로 조였다
                                     로그 — unknown drain policy clamped to graceful
                                            node=cp0-node want=gracefull stored=graceful
   policy 를 안 실은 광고             200.  nodes.drain = ""   해제다

오타가 하트비트를 안 끊는다. 400 을 냈으면 그 노드의 임대 갱신이 멈추고
not_after 가 지나 회수가 도는 Run 을 죽였을 것이다.


5. 뒤 유닛에 내주는 접점

   store.ListNodes(ctx) (NodePage, error)
   store.ListRuns(ctx, RunFilter) (RunPage, error)
   store.DrainingNodes(ctx) (map[string]bool, error)
   store.ListQueued(ctx) ([]QueuedRun, error)
   store.ValidState(s string) bool
   store.StateQueued
   contract.Policy{Drain string}
   contract.Advert.Policy
   (*Store).UpsertAdvert(...) (contract.Policy, error)   반환은 이전 정책이다
   store.StepView.Chosen

WakeQueued 는 없다 — queue.go 파일만 이 유닛이 만들었고 함수는
U-대기열이 같은 파일에 더한다.

5.1 뒤 유닛이 알아야 하는 것 둘

   조인 값이 한 왕복 뒤에 온다
       UpsertAdvert 는 이전 값을 낸다.  지금 값을 조인 결과로 알아야 하는
       쪽(광고 응답에 policy 를 싣는 U-drain)은 clampDrain 을 내보내거나
       자기가 다시 조여야 한다.  이 유닛은 그 함수를 안 내보냈다 —
       계획의 접점 목록에 없었다

   경계 검사기가 지금 규칙 하나만 센다
       internal/panel 과 internal/api/ui 가 아직 없어 셋이 SKIPPED 다.
       그 유닛이 패키지를 만들면 같은 명령이 실제 검사 결과를 낸다.
       고칠 것이 없다 — go test -v ./internal/boundary 가 CP1 부터
       "3 checked" 로 바뀌는 것을 보면 된다

6. API 문서

라우트가 안 늘었으므로 문서를 안 고친다. 한 줄만 는다.

   GET /v1/runs/{id}   응답의 steps[] 에 chosen 이 실린다 (ADR-060 §3).
                       없음과 false 를 안 가르려고 false 도 내보낸다

README.md 도 안 고친다 — 사용자에게 보이는 변화가 없다.