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 도 안 고친다 — 사용자에게 보이는 변화가 없다.