# Plan - REFACTOR Node Queue Simplify ## 이 파일을 읽는 구현 에이전트에게 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증 명령을 실행하고 실제 출력, 구현 메모, 계획 대비 변경 사항을 채운 뒤 active 파일을 남긴 채 리뷰 준비 상태로 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다. 구현 중 사용자만 결정할 수 있는 범위 변경, 외부 환경/secret, 또는 scope conflict가 생기면 chat에서 묻지 말고 review stub의 `사용자 리뷰 요청` 섹션에 근거와 재개 조건을 남긴다. ## 배경 현재 Node는 provider-scoped FIFO admission queue와 node-wide global gate를 소유한다. 현재 마일스톤의 목표는 queue owner를 Edge로 옮기는 것이므로, Node는 dispatch된 실행을 수행하고 상태를 보고하는 역할로 단순화되어야 한다. Edge queue core가 먼저 들어간 뒤 Node-local FIFO는 제거하거나 over-dispatch 안전장치 수준으로 축소한다. ## 사용자 리뷰 요청 흐름 구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하지 않는다. code-review가 해당 요청을 검증하고 필요할 때만 `USER_REVIEW.md`를 작성한다. ## Roadmap Targets - Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/edge-model-group-queue-scheduling.md` - Task ids: - `node-simplify`: Node-local provider FIFO queue가 제거되거나 Edge dispatch 이후의 실행 안전장치로 축소되어, 각 Node가 queue owner가 되지 않는다. - Completion mode: check-on-pass ## 분석 결과 ### 읽은 파일 - `agent-test/local/rules.md` - `agent-test/local/node-smoke.md` - `agent-test/local/edge-smoke.md` - `agent-ops/rules/project/domain/node/rules.md` - `agent-ops/rules/project/domain/testing/rules.md` - `apps/node/internal/node/node.go` - `apps/node/internal/node/run_manager.go` - `apps/node/internal/node/node_test.go` - `apps/node/internal/node/node_concurrency_integration_test.go` - `apps/node/internal/runtime/types.go` - `apps/node/internal/router/router.go` ### 테스트 환경 규칙 - test_env: `local`. - 적용 profile: `node-smoke`, `edge-smoke`. - 필수 명령: `go test -count=1 ./apps/node/internal/node`. - Node 실행 요청/stream/cancel/status 경로에 닿으므로 최종 regression subtask에서 repo 내부 edge-node 진단과 full-cycle 기준을 이어서 수행한다. ### 테스트 커버리지 공백 - 현재 Node tests는 Node-owned FIFO queue를 성공 조건으로 삼는 테스트가 많다. - Edge-owned queue 전환 뒤에는 Node가 queued status를 만들지 않아야 하므로 기존 `TestAdmissionQueue_*` 계열은 삭제, 이동, 또는 "over-dispatch immediate rejection/safety" 기준으로 전환해야 한다. - Node capabilities의 queued/in_flight 값은 Edge-owned snapshot과 충돌하지 않아야 한다. queued는 0 또는 safety-only 값으로 정리해야 한다. ### 심볼 참조 - 제거/축소 후보: `fifoGate`, `admissionManager`, `admissionTicket`, `ErrConcurrencyLimitExceeded` 주변 queue_full/queue_timeout helper. - call site: - `apps/node/internal/node/node.go:35` `globalGate` - `apps/node/internal/node/node.go:36-37` `adapterGates` - `apps/node/internal/node/node.go:98-213` admission enqueue/wait/launch flow - `apps/node/internal/node/node.go:216-242` admission helper - `apps/node/internal/node/node.go:332-344` capabilities in_flight/queued snapshot - `apps/node/internal/node/node_test.go:1438+` admission queue tests - `apps/node/internal/node/node_concurrency_integration_test.go:91+` queue overflow integration test ### 분할 판단 - shared task group: `m-edge-model-group-queue-scheduling`. - 현재 subtask: `03+01_node_queue_simplify`. - predecessor `01`: active `agent-task/m-edge-model-group-queue-scheduling/01_edge_queue_service/complete.log` 없음. 구현 시작 전 `01_edge_queue_service` PASS complete.log가 필요하다. - snapshot/surface 조정은 `02+01_surface_snapshot_contract`, final regression은 `04+01,02,03_regression_evidence`로 분리한다. ### 범위 결정 근거 - Node adapter provider probe, runtime adapter contracts, router lookup semantics는 바꾸지 않는다. - Edge scheduler 구현을 이 subtask에서 재설계하지 않는다. - 외부 OpenAI/A2A/console surface는 Node simplification 검증 범위가 아니므로 제외한다. ### 빌드 등급 - `cloud-G07`: 기존 Node admission queue는 cancellation, background run, timeout, store status, provider snapshot에 걸쳐 있어 대량 테스트 전환과 concurrency risk가 크다. ## 구현 체크리스트 - [ ] Node `OnRunRequest`에서 FIFO queue ownership을 제거하고 dispatch된 run을 즉시 실행 또는 safety reject로 처리한다. - [ ] Node capability snapshot의 queued/in-flight 의미를 Edge-owned queue와 충돌하지 않게 정리한다. - [ ] Node admission queue tests를 삭제/전환하고 over-dispatch safety, cancel, background, store lifecycle 테스트를 남긴다. - [ ] `go test -count=1 ./apps/node/internal/node`가 통과한다. - [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. ### [REFACTOR-1] OnRunRequest Queue Ownership 제거 #### 문제 Node가 provider FIFO queue owner다. ```go // apps/node/internal/node/node.go:98 98 // Admission gate (foreground and background use the same path). 110 admission := n.admissionFor(spec.Adapter, caps) 128 ticket, err := admission.enqueue() 167 if err := ticket.wait(execCtx); err != nil { 177 defer ticket.release() ``` #### 해결 방법 `OnRunRequest`는 router resolve, store insert, runManager register, adapter execute lifecycle만 맡긴다. capacity guard가 필요하면 queue 없는 non-blocking safety gate로 두고, full이면 즉시 error event와 rejected store status를 남긴다. queued store status는 Edge queue가 소유하므로 Node-local 대기 상태로 쓰지 않는다. #### 수정 파일 및 체크리스트 - [ ] `apps/node/internal/node/node.go`: admission enqueue/wait 흐름 제거 또는 safety guard로 축소. - [ ] `apps/node/internal/node/run_manager.go`: FIFO queue helper 제거 또는 safety-only helper로 축소. #### 테스트 작성 - 작성: `TestOnRunRequest_DispatchedRunRunsImmediately`, `TestOnRunRequest_OverDispatchSafetyRejectsWithoutQueue` 또는 기존 테스트 전환. #### 중간 검증 ```bash go test -count=1 ./apps/node/internal/node -run 'TestOnRunRequest' ``` ### [REFACTOR-2] Capability Snapshot 정리 #### 문제 Node command capabilities가 Node-local gate의 in-flight/queued를 보고한다. ```go // apps/node/internal/node/node.go:332 332 inFlight := 0 333 queued := 0 334 if ok { 335 inFlight = gate.activeCount() 336 queued = gate.queuedCount() 337 } ``` Edge-owned snapshot과 섞이면 queued 의미가 충돌한다. #### 해결 방법 Node는 provider availability/capacity와 safety in-flight만 보고하고 queued는 0으로 고정하거나 safety queue가 없음을 명시한다. Edge-owned queued는 `02`의 service snapshot이 표현한다. #### 수정 파일 및 체크리스트 - [ ] `apps/node/internal/node/node.go`: capabilities result/snapshot queued semantics 정리. - [ ] `apps/node/internal/node/node_test.go`: capabilities queued 테스트 전환. #### 테스트 작성 - 작성/전환: `TestOnCommandRequest_Capabilities`, `TestOnCommandRequest_Capabilities_InFlight`, 기존 `TestOnCommandRequest_Capabilities_Queued` 삭제 또는 safety-only 이름으로 변경. #### 중간 검증 ```bash go test -count=1 ./apps/node/internal/node -run 'TestOnCommandRequest_Capabilities' ``` ### [REFACTOR-3] Admission Queue Test 전환 #### 문제 `apps/node/internal/node/node_test.go:1438+` 이후는 Node-owned FIFO를 전제로 한다. Edge queue 전환 뒤 이 테스트들이 그대로 남으면 목표와 반대 동작을 고정한다. #### 해결 방법 FIFO ordering, max_queue, queue_timeout, release promotion 테스트는 제거하거나 Edge service queue tests로 이동된 것을 확인한다. Node에는 cancel/background/store/error lifecycle과 over-dispatch immediate safety만 남긴다. #### 수정 파일 및 체크리스트 - [ ] `apps/node/internal/node/node_test.go`: `TestAdmissionQueue_*` 계열 정리. - [ ] `apps/node/internal/node/node_concurrency_integration_test.go`: queue overflow integration test를 safety rejection 또는 제거로 전환. #### 테스트 작성 - 작성: over-dispatch safety event가 Edge로 도착하는 통합 테스트가 필요하면 기존 `TestQueueOverflow_RejectEventObservedByEdge`를 재명명해 즉시 rejection 기준으로 유지한다. #### 중간 검증 ```bash go test -count=1 ./apps/node/internal/node -run 'Test(AdmissionQueue|ConcurrencyLimit|QueueOverflow|OnRunRequest|OnCommandRequest_Capabilities)' ``` ## 의존 관계 및 구현 순서 - `01_edge_queue_service` complete.log가 먼저 필요하다. - 구현 전 확인 후보: - active: `agent-task/m-edge-model-group-queue-scheduling/01_edge_queue_service/complete.log` - archive: `agent-task/archive/*/*/m-edge-model-group-queue-scheduling/01_edge_queue_service/complete.log` ## 수정 파일 요약 | 파일 | 항목 | |------|------| | `apps/node/internal/node/node.go` | REFACTOR-1, REFACTOR-2 | | `apps/node/internal/node/run_manager.go` | REFACTOR-1 | | `apps/node/internal/node/node_test.go` | REFACTOR-1, REFACTOR-2, REFACTOR-3 | | `apps/node/internal/node/node_concurrency_integration_test.go` | REFACTOR-3 | ## 최종 검증 ```bash go test -count=1 ./apps/node/internal/node ``` 기대 결과: 통과. Go test cache는 허용하지 않는다. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.