문서와 심볼

aidlc-docs 를 그대로 읽는다 · 읽기 전용
/home/sunny/enode-fixup-workshop2-visualizer연결 중
inception/plans/unit-of-work-plan.md222 줄

유닛 계획 — Units Generation Part 1

유닛을 나누는 계획이 아니다. 유닛은 이미 다섯으로 정해져 있고 승인됐다
(aidlc-state.md 「실행 계획」 4절 — 권장 여섯에서 U-화면을 U-관측API 와
U-제어판에 합쳤다). 이 계획이 하는 일은 둘이다.

   1   그 다섯을 v1 이 요구하는 산출물 셋으로 낸다
   2   유닛 둘 이상이 읽는 접점 중 아직 모양이 하나로 안 정해진 셋을 닫는다

둘째가 이 계획의 질문이다. requirements.md §8.4 끝의 「답 A 가 덜 정한
자리 셋」이 그것이고, 셋 다 유닛 둘이 같은 필드를 읽는 자리라 Functional
Design 으로 내려보내면 유닛마다 다른 답이 나온다.


1. 질문 셋

권장안을 A 에 뒀다. 그대로면 A 만 적으면 된다.

Question 1

GET /v1/runs 행의 current 는 단수인가 배열인가.

requirements.md §8.4 ① 이 current{step, attempt, started_at} 으로 답을
받았다. 그런데 같은 필드를 화면 둘이 읽고 둘이 세는 단위가 다르다.

   S1 카드     노드 하나다.   이 노드가 지금 무슨 단계를 도는가
   Run 행      Run 하나다.    이 Run 이 지금 무슨 단계를 도는가

폭이 1 인 Run 에서는 둘이 같다. 폭이 1 을 넘으면 갈린다 — steps.node_id
가 단계마다 다르고 임대는 노드마다인데 Run 은 하나다. 카드가 lease.run_id
로 조인해서 단수 current 를 읽으면 남의 노드가 도는 단계를 자기 카드에
그릴 수 있다.

A) 배열로 낸다. current: [{node, step, attempt, started_at}] — 지금
CLAIMED 인 단계마다 한 항목이고 도는 것이 없으면 안 낸다. S1 카드는
node 가 자기 것인 항목을 고르고, Run 행은 started_at 이 가장 이른 항목을
적는다. 폭이 1 이면 항목이 하나라 지금 그림과 같아 보인다. steps 조인에
node_id 열이 하나 더 실릴 뿐이고 새 열은 여전히 0 이다

B) 단수로 두고 가장 이른 CLAIMED 단계 하나만 낸다. 응답이 얇다. 대신
폭이 1 을 넘는 Run 에서 카드가 틀린 단계를 보인다. 이 팩의 CP3 계약이 「단계
둘 이상」을 요구하므로(scene-gates.md §3) CP3 에서 실제로 걸린다

C) 단수로 두되 GET /v1/nodes 의 lease 에 step 을 넣어 카드를 먹인다.
§8.4 ① 이 기각한 B 안으로 돌아가는 것이다 — ADR-065 개정 범위가 draining
하나에서 lease 여러 필드로 늘고 정본 쪽 PR 이 커진다

X) Other (please describe after Answer: tag below)


Question 2

reject 의 키 대소문자를 어떻게 하는가.

match.Reject 에 json 태그가 없다 (internal/match/match.go:49). 그래서
기존 GET /v1/runs/{id} 가 지금 이렇게 낸다 (internal/api/api.go:155).

   지금        "reject": { "Code": 422, "As": "builder", "Reason": "…" }
   §8.4 ① 이 적은 것   reject{code, as, reason}

목록 라우트를 소문자로 내면 같은 것이 라우트 둘에서 다른 이름이 된다.

대조로 확인한 것 셋 — 저장소 안에서 runView.Reject 의 키를 읽는 코드가
없다 (runctl 이 읽는 것은 별개 타입 errBody 이고 그쪽은 이미 소문자다).
enode-design/protocol/mediator-api.md 의 GET /v1/runs/{id} 절이 reject 를
아예 안 적었다 — 개정할 문장이 없다. DB 의 옛 행은 {"Code":…} 로 들어
있으나 Go 의 언마샬이 키 대소문자를 무시하므로 태그를 달아도 그대로 읽힌다.

A) match.Reject 에 json 태그를 단다. 한 줄씩 셋이고 두 라우트가 소문자로
합쳐진다. constraints.md §7 의 「기존 계약 변경」 목록에 없는 자리이고 —
그 목록은 success_when 문법 · 봉인 규칙 · 임대 키 · READY · drain 중앙
라우트 · I1·I2·I4·I5 다 — 소비자가 0 이라 깨질 것이 없다. 근거를
적고 넘는다

B) 새 목록 라우트도 {Code, As, Reason} 그대로 낸다. 기존 라우트를 한
글자도 안 건드린다. 대신 새 화면이 run_id 옆에서 reject.Reason 을 읽는다 —
나중에 누군가 「고쳐야지」 하고 손대면 그때 갈린다

C) 목록 라우트만 소문자로 매핑한다. 기존 라우트도 안 건드리고 새 응답도
깨끗하다. 대신 같은 것에 이름이 둘이 된다 — 이 팩이 §4.2 에서 이미 기각한
모양이다

X) Other (please describe after Answer: tag below)


Question 3

제어판이 Mediator 를 어느 범위로 폴링하는가.

§8.4 ② 로 serve 가 이 기계의 노드를 전부 서브하게 됐고, §8.4 ③ 으로 노드마다
마지막 관측을 메모리에 들게 됐다. 둘이 만나는 자리에 폴링 범위가 있다 —
캐시가 차는 범위가 여기서 갈린다.

A) serve 가 5초마다 GET /v1/nodes 를 한 번 받아 이 기계 노드 전부의
행을 캐시에 넣는다.
/api/status?node= 는 캐시를 읽는다. Mediator 로 나가는
요청이 노드 수와 무관하게 5초에 하나이고, 사이드바에서 안 열어 본 노드의
「마지막 관측」도 찬다 — 그래야 멎은 노드를 열었을 때 S5 가 그림대로 그려진다.
GET /v1/runs/{id}(현재 작업)는 화면이 보고 있는 노드에 임대가 있을 때만 부른다

B) /api/status?node= 가 올 때마다 Mediator 를 부른다. 캐시는 그 응답의
사본일 뿐이다. 화면이 한 번에 노드 하나를 보므로 요청 수는 역시 5초에 하나다.
대신 열어 본 적 없는 노드의 캐시가 비어 있다 — 데몬이 멎고 광고가 만료된
뒤에 그 노드를 처음 열면 「마지막 관측」이 영영 — 다

X) Other (please describe after Answer: tag below)


2. 묻지 않은 범주와 근거

v1 은 여섯 범주를 전부 평가하라고 요구한다. 셋만 물었으므로 나머지에 근거를
적는다
— 안 물은 것이 빠뜨린 것이 아니라 이미 닫힌 것임을 여기서 보인다.

범주 판정 근거
Story Grouping 닫힘 스토리가 없다. User Stories 를 건너뛴 근거가 scene-gates.md §1 이 이미 페르소나 둘의 장면이라는 것이고, 묶는 단위는 스토리가 아니라 조각 게이트 CP0 ~ CP4 다. 유닛과 게이트의 대응은 실행 계획 4절이 고정했다
Dependencies 닫힘 의존 넷이 실행 계획 5절에 있고 접점 아홉이 requirements.md §4.1 에 값으로 있다. 직렬로 받아야 하는 파일(internal/api/api.go 등록 줄)의 처리는 unit-boundaries.md §3
Team Alignment 해당 없음 배치 폭이 1 이다. 유닛을 나눠 맡을 사람이 없고 병합 순서가 곧 착수 순서다 (START.md §5). U-화면을 합친 근거가 이것이다
Technical Considerations 닫힘 배포 모델이 안 바뀐다 — 새 실행파일 없음 · 클라우드 자원 없음 · packaging/ 안 건드림. 유닛마다 다른 확장성 요구가 없다. 스택은 Go 1.26 · 표준 ServeMux · PostgreSQL 로 고정이고 새 의존이 0 이다
Business Domain 닫힘 경계가 도메인이 아니라 표면으로 갈린다 — 중앙(관측·대기열)과 노드(drain·제어판). 그 선은 ADR-042 가 「경계는 노드에 있다」로 이미 그었고 design/README.md §1 이 표면별 책임을 갈랐다
Code Organization 해당 없음 Brownfield 다. 디렉터리 구조가 이미 있고 새 패키지 둘의 자리도 unit-boundaries.md §2 의 파일 행렬이 지목했다 — internal/panel · internal/api/ui

3. Part 2 실행 단계

승인 뒤에 이 순서로 돈다. 완료 즉시 그 자리에서 [x] 로 바꾼다.

  • 3.1 질문 셋의 답을 requirements.md 에 값으로 반영한다 — §8.4 끝의
    「덜 정한 자리 셋」을 결정으로 바꾸고, §4.1 의 계약 줄과 §4.3 의 라우트에
    값을 싣는다. 답이 권장을 벗어나면 근거를 적는다 (결정표 게이트 규칙)
  • 3.2 aidlc-docs/inception/application-design/unit-of-work.md 를 낸다.
    유닛 다섯마다 다음을 적는다
    • 3.2.1 하는 일 · 출처 요구사항 절 (enode-features.md 절 번호와
      requirements.md 의 FR 번호)
    • 3.2.2 딛는 유닛 · 그 유닛에서 받는 것
    • 3.2.3 만지는 파일. 신설은 NEW 로 표시한다 (unit-boundaries.md
      §2 의 행렬을 유닛 쪽으로 뒤집은 것)
    • 3.2.4 완료 조건. 화면이 있는 유닛은 그 화면의 버튼을 전부
      나열한다
      — 재료는 requirements.md §8.3 이다 (scene-gates.md §2.1)
    • 3.2.5 조각 게이트 — 이 유닛이 초록으로 만드는 CP 번호와 그 명령
  • 3.3 unit-of-work-dependency.md 를 낸다 — 의존 행렬 · 파일 겹침
    행렬과 겹치는 자리의 처리 · 접점 표(§4.1 의 계약을 유닛 쌍에 붙인다) ·
    착수 순서 · 직렬로 받는 자리
  • 3.4 unit-of-work-story-map.md 를 낸다. 스토리가 없으므로
    장면 게이트를 맵의 축으로 쓴다
    — FR 25개 · 화면 일곱 · CP0 ~ CP4 를
    유닛에 건다. 파일 이름은 v1 의 것을 그대로 쓴다 (아래 4절)
  • 3.5 U-바닥의 완료 조건에 경계 검사 테스트를 박는다 —
    unit-boundaries.md §5 의 금지 임포트 넷. CP0 가 기계로 센다
  • 3.6 검증한다
    • 3.6.1 FR-1 ~ FR-25 가 전부 어느 유닛엔가 배정됐다. 빠진 번호 0
    • 3.6.2 §4.1 의 계약마다 내는 유닛 하나가 있다. 주인 없는 계약 0
    • 3.6.3 파일 행렬에서 한 파일을 유닛 둘이 만지면 처리 문장이 있다
    • 3.6.4 다이어그램과 표를 common/content-validation.md 로 검증한다
  • 3.7 aidlc-state.md 의 Phase Status 와 Current Status 를 갱신하고
    audit.md 에 기록한다 (덮어쓰지 않고 덧붙인다)

4. 산출물 셋 — 경로와 이름

v1 이 요구하는 경로를 그대로 쓴다.

   aidlc-docs/inception/application-design/unit-of-work.md
   aidlc-docs/inception/application-design/unit-of-work-dependency.md
   aidlc-docs/inception/application-design/unit-of-work-story-map.md

Application Design 단계는 건너뛰는데 디렉터리 이름이 application-design/
인 것
은 v1 의 규칙이 유닛 산출물을 그 자리에 두기 때문이다. 이름을 바꾸면
뒤 단계가 읽는 경로와 갈린다. 디렉터리에는 유닛 산출물 셋만 들어가고
컴포넌트 설계 문서는 안 들어간다 — 그것을 안 만드는 근거는 실행 계획 3절이다.

unit-of-work-story-map.md 도 이름을 그대로 둔다. 안에 들어가는 것은
스토리가 아니라 장면 게이트와 화면과 FR 번호다.
파일 머리에 그 사실을
적는다 — 이름과 내용이 어긋난 채로 두면 다음에 읽는 사람이 스토리를 찾는다.


5. 이 계획이 안 하는 것

   유닛을 다시 가르는 것        실행 계획 4절이 승인됐다.  다섯이다
   메서드 수준 설계             유닛마다의 Functional Design 몫이다 (실행 계획 3절)
   정책 파일의 위치와 형식       decisions.md 1절이 명시적으로 Functional Design 에 뒀다
   코드                        Code Generation 이 유닛마다 따로 계획을 낸다

6. 보안 확장 준수

security-baseline 이 켜져 있다 (aidlc-state.md Extension Configuration).
이 단계의 산출은 문서이므로 규칙 대부분이 해당 없음이고, 유닛 산출물이
새 표면의 보안 요구를 완료 조건으로 지고 있는가
만 이 단계에서 센다.

   SECURITY-08  접근 제어      U-제어판 완료 조건에 「127.0.0.1 밖에서 안 붙는다」
   SECURITY-12  인증           U-관측API 완료 조건에 두 라우트의 토큰 보호
                              U-제어판 완료 조건에 --listen 이 밖이면 panel_token 필수
   SECURITY-05  입력 검증      U-관측API 완료 조건에 limit 상한 1000 · 정책 값 enum
   SECURITY-04  보안 헤더      U-관측API · U-제어판 완료 조건에 헤더 넷
                              (requirements.md §3.3.  CSP 가 인라인 JS·CSS 를 막는다)
   나머지                      해당 없음.  이 단계는 코드를 안 낸다

3.6 을 통과할 때 이 다섯 줄이 산출물에 실제로 들어갔는지 확인한다. 안 들어가
있으면 차단성 발견이라 단계 완료를 안 낸다.