diff --git a/Makefile b/Makefile index 2c64178..c529e4b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all build tidy test proto clean +.PHONY: all build tidy test test-e2e proto clean GOFLAGS ?= -trimpath @@ -16,6 +16,9 @@ tidy: test: go test ./... +test-e2e: + ./scripts/e2e-smoke.sh + # Requires: protoc + protoc-gen-go (go install google.golang.org/protobuf/cmd/protoc-gen-go@latest) proto: protoc \ diff --git a/README.md b/README.md index 9fbc89f..a8ad790 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ Edge의 핵심 역할은 다음과 같다. Edge는 자신의 데이터를 자체적으로 가진다. Control Plane은 Edge의 데이터를 조회하고 조정하지만, 런타임 데이터의 원본은 Edge다. -현재 edge 내부에서는 console을 최종 사용자 인터페이스가 아니라 임시 디버그 클라이언트로 본다. Console의 `/` 명령은 `apps/edge/internal/service`를 호출하는 얇은 어댑터이며, 향후 HTTP/API handler도 같은 service를 호출하는 방향이다. 실행 이벤트와 node lifecycle 이벤트는 `apps/edge/internal/events` bus를 통해 fanout한다. +현재 edge 내에는 edge-local ops console이 있다. ops console의 `/` 명령은 `apps/edge/internal/service`를 호출하는 얇은 어댑터이며, 향후 HTTP/API handler도 같은 service를 호출하는 방향이다. HTTP/API를 central/remote management surface로, ops console을 edge-local diagnostic surface로 구분한다. 실행 이벤트와 node lifecycle 이벤트는 `apps/edge/internal/events` bus를 통해 fanout한다. ### Node @@ -195,8 +195,8 @@ NomadCode - Edge-Node 소켓 기반 구조를 우선 검증 중이다. - Node 등록, 설정 전달, 실행 요청, 스트리밍 이벤트 흐름이 점진적으로 정리되고 있다. - Edge 내부에는 API 전환을 고려한 `apps/edge/internal/service`와 in-process event fanout인 `apps/edge/internal/events`가 있다. -- CLI adapter 쪽 구현이 먼저 진행되고 있다. -- edge console의 `/` 명령은 임시 수동 테스트 UI이며, 장기 인터페이스는 별도 HTTP/API 표면으로 추가한다. +- cli adapter(node execution implementation) 쪽 구현이 먼저 진행되고 있다. +- edge-local ops console의 `/` 명령은 수동 테스트 표면이며, 장기 인터페이스는 별도 HTTP/API 표면으로 추가한다. - 현재 실행 이력은 Node local SQLite store에서 검증 중이다. Edge 단위 이력 집계와 로컬 실행 그룹 상태 소유권은 로드맵에 따라 정리한다. - `mock` adapter와 dummy/TODO 구현은 개발 단계에서 정상적인 구성이다. - Ollama/vLLM 등 모델 runtime adapter는 단계적으로 확장한다. diff --git a/agent-ops/rules/project/domain/edge/rules.md b/agent-ops/rules/project/domain/edge/rules.md index 4c5e747..f893ef7 100644 --- a/agent-ops/rules/project/domain/edge/rules.md +++ b/agent-ops/rules/project/domain/edge/rules.md @@ -15,7 +15,7 @@ ## 제외 경로 - `apps/node/` — 실제 모델 실행과 adapter 관리 -- `apps/control-plane/` — 여러 Edge 연결 관리와 운영 화면 제공 예정 영역 +- `apps/control-plane/` — 여러 Edge 연결 관리와 운영 제어 API 제공 예정 영역 - `apps/worker/` — 비동기 작업 처리 예정 영역 - `packages/` — 공통 설정/관측성/인증 패키지 - `proto/` — 메시지 계약 원본과 생성물 diff --git a/agent-ops/rules/project/domain/node/rules.md b/agent-ops/rules/project/domain/node/rules.md index 5a28381..e160784 100644 --- a/agent-ops/rules/project/domain/node/rules.md +++ b/agent-ops/rules/project/domain/node/rules.md @@ -18,7 +18,7 @@ Edge에 연결되어 실제 adapter execution을 수행하는 IOP 노드 에이 ## 제외 경로 - `apps/edge/` — Node를 관리하는 실행 그룹 컨트롤러 영역 -- `apps/control-plane/` — 여러 Edge 연결 관리와 운영 화면 제공 예정 영역 +- `apps/control-plane/` — 여러 Edge 연결 관리와 운영 제어 API 제공 예정 영역 - `apps/worker/` — 비동기 작업 처리 예정 영역 - `packages/` — 여러 앱이 공유하는 공통 패키지 - `proto/` — 앱 간 메시지 계약 diff --git a/agent-ops/rules/project/rules.md b/agent-ops/rules/project/rules.md index c25079a..5c8c522 100644 --- a/agent-ops/rules/project/rules.md +++ b/agent-ops/rules/project/rules.md @@ -16,7 +16,7 @@ - `apps/node/` — Edge에 연결되는 실행자. 런타임 라우팅, adapter execution, CLI/model runtime 실행, 현재 단계의 로컬 실행 이력 저장을 담당한다. - `apps/edge/` — 여러 Node를 묶는 백엔드 실행 그룹 컨트롤러. token 기반 등록, node registry, node 설정 전달, routing, stream relay를 담당한다. -- `apps/control-plane/` — 향후 여러 Edge를 연결하고 상태 조회/설정 변경/명령 전달/이벤트 수신/프론트 페이지 제공을 담당할 중앙 관리 계층이다. 현재 placeholder이다. +- `apps/control-plane/` — 향후 여러 Edge를 연결하고 상태 조회/설정 변경/명령 전달/이벤트 수신/운영 제어 API 제공을 담당할 Go 기반 중앙 관리 서버이다. 현재 placeholder이다. - `apps/worker/` — 비동기 작업 처리 예정 영역이다. 현재 placeholder이다. - `packages/` — 설정, 인증, 정책, 메타데이터, 작업, 관측성, 버전 등 공통 패키지이다. - `proto/iop/` — IOP 메시지 계약 원본이다. @@ -49,7 +49,7 @@ - 앱 설정 구조 변경 시 `packages/config`의 struct/default와 `configs/*.yaml` 예시를 함께 확인한다. - 테스트는 변경 범위에 맞춰 `go test ./...` 또는 대상 패키지 테스트를 실행한다. - 사용자 실행 파이프라인에 닿는 작업을 한 경우, 작업 완료 후 `agent-ops/rules/project/domain/testing/rules.md`의 검증 기준을 따른다. -- 상세 DB schema, event schema, permission/policy/audit model, federation, mTLS 구현 세부, Control Plane UI 세부 기획은 각 작업에서 별도로 결정한다. +- 상세 DB schema, event schema, permission/policy/audit model, federation, mTLS 구현 세부, Portal UI 세부 기획은 각 작업에서 별도로 결정한다. ## 도메인 룰 로딩 @@ -71,7 +71,7 @@ ## 도메인 후보 -- `control-plane`: `apps/control-plane/**`가 placeholder를 넘어 여러 Edge 연결 관리, Edge 상태 조회, Edge 설정 변경, Edge 명령 전달, 이벤트 수신, 프론트 페이지 제공을 구현하기 시작할 때 생성한다. +- `control-plane`: `apps/control-plane/**`가 placeholder를 넘어 여러 Edge 연결 관리, Edge 상태 조회, Edge 설정 변경, Edge 명령 전달, 이벤트 수신, 운영 제어 API 제공을 구현하기 시작할 때 생성한다. - `worker`: `apps/worker/**`가 placeholder를 넘어 작업 큐 소비/재시도/결과 저장을 구현하기 시작할 때 생성한다. ## 스킬 라우팅 diff --git a/agent-ops/skills/common/code-review/SKILL.md b/agent-ops/skills/common/code-review/SKILL.md index a09ca9c..7a9ec8e 100644 --- a/agent-ops/skills/common/code-review/SKILL.md +++ b/agent-ops/skills/common/code-review/SKILL.md @@ -26,6 +26,12 @@ Filename rules: - `{lane}` is only `local` or `cloud`; never put model names in filenames. - `GNN` is a two-digit capability grade from `G01` to `G10`; runtime maps lane+grade to current models externally. +Multi-plan runtime contract: + +- Multi-plan work is represented as multiple task directories. Each directory owns exactly one normal active plan file and one normal active review file. +- Directory names may encode runtime scheduling metadata. Preserve them verbatim; do not normalize, reinterpret, or choose execution order by agent judgment. +- If the user/runtime names a task directory, review that directory even when other active review files exist. + Review routing rules: - `local`: narrow, low-risk, or first-pass review where tests and scope are clear. @@ -37,12 +43,22 @@ Directory states: | State | Meaning | |-------|---------| -| `PLAN-*-G??.md` + `CODE_REVIEW-*-G??.md` | Ready for review | +| `PLAN-*-G??.md` + `CODE_REVIEW-*-G??.md` stub or placeholders | Implementation is pending/incomplete; review should fail completeness if invoked | +| `PLAN-*-G??.md` + filled `CODE_REVIEW-*-G??.md` | Ready for code-review skill | | `complete.log` + `*.log` files | Task complete (PASS) | | Only `*.log` files (no `complete.log`) | Task terminated mid-loop or abandoned | The implementing agent never archives or deletes active files; archiving is this skill's responsibility. +Finalization invariant: + +- Every review attempt that selects an active review file must end by archiving the active `CODE_REVIEW-*-G??.md` and `PLAN-*-G??.md` files. +- After archiving, exactly one next state is allowed: + - `PASS`: write `complete.log` and leave no active `.md` files. + - `WARN` or `FAIL`: write the next active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md`; do not write `complete.log`. +- Never stop after appending a verdict. Do not report to the user until archive and the required next-state file writes are complete. +- If the review result feels ambiguous, choose `WARN` or `FAIL` according to the severity rules, archive the current files, and write the next plan/review pair. Ambiguity is not a reason to leave active files unarchived. + ## Step 1 - Find Active Task Glob `agent-task/*/CODE_REVIEW-*-G??.md`: @@ -51,7 +67,7 @@ Glob `agent-task/*/CODE_REVIEW-*-G??.md`: |--------|--------| | Exactly one | Review that task; exactly one `PLAN-*-G??.md` is expected beside it | | None | Nothing to review; stop and report | -| Multiple | List paths and ask which task to review | +| Multiple | If the user/runtime named a task directory, review that directory. Otherwise list paths and ask which task to review; do not choose by agent judgment. | ## Step 2 - Load Context @@ -67,6 +83,9 @@ The diff is the starting point, not the boundary. Follow behavior and API connec Before writing the verdict: - Compare actual source files against every planned checklist item. +- Compare the plan `구현 체크리스트` and review stub `구현 체크리스트`; item text/order must match. +- Confirm the implementation marked the matching checklist items in the active review file, including the final mandatory `CODE_REVIEW-*-G??.md` completion item. +- Treat blank placeholder sections, missing actual implementation notes, missing checklist completion, or missing actual stdout/stderr in the active review file as a completeness or verification-trust failure. - Grep renamed/removed symbols for stale references. - Confirm every required test exists, name matches, and assertions are meaningful. - Cross-check claimed verification output in the active review file against actual code and project commands. @@ -83,6 +102,8 @@ Required fields: - `발견된 문제`: `없음`, or bullets using `Required`, `Suggested`, or `Nit` with `file:line` and a concrete fix. - `다음 단계`: keep only the matching PASS/WARN/FAIL line. +Do not check archive/next-state items in `코드리뷰 전용 체크리스트` during Step 4. Complete that dedicated checklist in the archived `code_review_*.log` during Step 7, after archive and next-state writes are done. + Severity semantics: | Verdict | Meaning | Follow-up plan | @@ -93,13 +114,19 @@ Severity semantics: Issue severity: -- `Required`: correctness, API contract, missing required test, or plan-completeness issue. +- `Required`: correctness, API contract, missing required test, plan-completeness issue, or incomplete/placeholder `CODE_REVIEW-*-G??.md` content required from the implementing agent. - `Suggested`: useful improvement that should enter the loop but does not block correctness. - `Nit`: tiny cleanup; may be recorded without forcing WARN. +Verdict consistency: + +- `PASS` requires all dimensions to be Pass and no Required/Suggested issues. Nit-only findings may still PASS only when every dimension remains Pass. +- Any Fail dimension or any Required issue forces `FAIL`. +- Any Warn dimension or any Suggested issue forces `WARN`, unless the only findings are explicitly Nit and every dimension remains Pass. + ## Step 5 - Archive Active Files -Archive order is fixed: +Archive is mandatory for `PASS`, `WARN`, and `FAIL`. Archive order is fixed: 1. Count existing `code_review_*.log` as `N`; rename `CODE_REVIEW-{review_lane}-GNN.md` to `code_review_{review_lane}_GNN_N.log`. 2. Count existing `plan_*.log` as `M`; rename `PLAN-{build_lane}-GNN.md` to `plan_{build_lane}_GNN_M.log`. @@ -117,7 +144,7 @@ Required fields in `complete.log`: - `최종 리뷰 요약`: bullet list of what was implemented. - `잔여 Nit`: any Nit-only findings recorded but not acted on (omit section if none). -Then report: +After Step 7, report: - Verdict. - Archive filenames. @@ -137,6 +164,7 @@ For `WARN` or `FAIL`, write new routed plan/review files using the plan skill fo - `FAIL`: one plan item per Required issue. - `WARN`: one grouped plan item for Suggested issues, plus related Nit issues if useful. - Each plan item needs problem, solution with before/after when non-trivial, checklist, test decision, intermediate verification. +- The follow-up plan and review stub must contain matching `구현 체크리스트` item text/order, including the final mandatory `CODE_REVIEW-*-G??.md` completion item. Routed review stub template (fill `{…}` placeholders; everything else is fixed and must not be changed by the implementing agent): @@ -145,6 +173,12 @@ Routed review stub template (fill `{…}` placeholders; everything else is fixed # Code Review Reference - {TAG} +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** +> The task is NOT complete until every implementation-owned section below is filled in. +> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. +> Do not modify or check the `코드리뷰 전용 체크리스트`; it is owned by the review agent only. +> Follow the ownership table at the bottom of this file for which sections you own. + ## 개요 date={YYYY-MM-DD} @@ -159,6 +193,9 @@ task={task_name}, plan={N}, tag={TAG} 2. `PLAN-{build_lane}-GNN.md` → `plan_{build_lane}_GNN_M.log` (M = 기존 plan_*.log 수) 3. PASS인 경우 `complete.log` 작성 후 종료. WARN/FAIL인 경우 새 routed plan + review 스텁 작성. +어떤 판정에서도 아카이브를 건너뛰지 마세요. PASS/WARN/FAIL 모두 `코드리뷰 결과` append 후 active plan/review 파일을 먼저 아카이브하고, 그 다음 `complete.log` 또는 다음 plan/review 파일을 작성해야 합니다. +아카이브와 다음 상태 파일 작성이 끝난 뒤, archived `code_review_*.log`의 `코드리뷰 전용 체크리스트`를 모두 체크한 다음 보고하세요. + --- ## 구현 항목별 완료 여부 @@ -168,6 +205,22 @@ task={task_name}, plan={N}, tag={TAG} | [{TAG}-1] {item description} | [ ] | | [{TAG}-2] {item description} | [ ] | +## 구현 체크리스트 + +{copy the follow-up plan's 구현 체크리스트 items exactly, preserving order and checkbox text} + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. +- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] WARN/FAIL이면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. + ## 계획 대비 변경 사항 _구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ @@ -200,6 +253,12 @@ $ {verification command from plan} $ {final verification command from plan} (output) ``` + +--- + +> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?** +> If anything is blank, go back and fill it in before saving this file. +> Leave the review-agent-only checklist unchanged. ``` Sections and their ownership: @@ -208,19 +267,31 @@ Sections and their ownership: |------|--------|------| | 헤더 주석, 개요(date/task/plan/tag), 리뷰 에이전트 지시 | 스텁 생성 시 고정 | 구현 에이전트가 수정하지 않음 | | 구현 항목별 완료 여부 (항목명) | 스텁 생성 시 고정 | `[ ]` → `[x]` 체크만 구현 에이전트가 수행 | +| 구현 체크리스트 (항목 텍스트/순서) | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 `[ ]` → `[x]` 체크만 수행; 마지막 체크박스는 저장 전 필수 | +| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section | | 계획 대비 변경 사항, 주요 설계 결정 | 구현 에이전트가 채움 | placeholder 텍스트를 실제 내용으로 교체 | | 리뷰어를 위한 체크포인트 | 스텁 생성 시 고정 | 계획에서 추출한 리뷰 포인트 | | 검증 결과 (섹션 제목 + 명령) | 스텁 생성 시 고정 | 실행 출력만 구현 에이전트가 채움; 명령 변경은 `계획 대비 변경 사항`에 기록 | | 코드리뷰 결과 | 리뷰 에이전트가 append | 스텁에 포함하지 않음 | -Report Required/Suggested counts, archive names, and the new plan path. +## Step 7 - Complete Review-Only Checklist And Report + +After Step 6: + +- Open the archived `code_review_{review_lane}_GNN_N.log`. +- Check every item in `코드리뷰 전용 체크리스트`. +- If any item cannot be checked, finish the missing archive, `complete.log`, or follow-up plan/review write first. +- Do not recreate an active review file just to update this checklist; update the archived `code_review_*.log`. +- Only report after the archived review log has the verdict, checked review-only checklist, and the required next-state files. + +Report Required/Suggested counts, archive names, and the `complete.log` path or new plan path. ## Review Dimensions | Dimension | Check | |-----------|-------| | Correctness | Logic, edge cases, concurrency, errors | -| Completeness | All planned checklist items done | +| Completeness | All planned checklist items done, including matching plan/review `구현 체크리스트` completion | | Test coverage | Required tests present and meaningful | | API contract | Call sites, compatibility, docs | | Code quality | No debug prints, dead code, leftover TODOs | @@ -242,4 +313,5 @@ Report Required/Suggested counts, archive names, and the new plan path. - `plan_{build_lane}_GNN_M.log` exists. - No active `.md` files remain after PASS. - PASS: `complete.log` written with loop history, implementation summary, and residual Nits. -- WARN/FAIL: new active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md` created with matching headers; no `complete.log`. +- WARN/FAIL: new active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md` created with matching headers and matching `구현 체크리스트`; no `complete.log`. +- The review-agent-only finalization checklist was completed before reporting. diff --git a/agent-ops/skills/common/plan/SKILL.md b/agent-ops/skills/common/plan/SKILL.md index 71118ab..a22e26e 100644 --- a/agent-ops/skills/common/plan/SKILL.md +++ b/agent-ops/skills/common/plan/SKILL.md @@ -26,6 +26,17 @@ Filename rules: - `{lane}` is only `local` or `cloud`; never put model names in filenames. - `GNN` is a two-digit capability grade from `G01` to `G10`; the runtime maps lane+grade to current models externally. +Task directory naming rules: + +- A single-plan task uses `agent-task/{task_name}/` with a short snake_case task name. +- If one plan is not enough, split the work into multiple task directories. Each directory owns exactly one normal active plan file and one normal active review stub. +- Multi-plan output is a set of independent `PLAN-{build_lane}-GNN.md` + `CODE_REVIEW-{review_lane}-GNN.md` pairs across multiple folders, not multiple plan files inside one folder. +- Multi-plan task directory names must start with an execution-order index: `01_{task_name}`, `02_{task_name}`, `03_{task_name}`, and so on. +- If a later task cannot start until a specific current task is complete, mark that dependency immediately after the current task's index with `+`, not `_`: `01+{dependent_task_name}` means this task depends on completion of the `01_...` task. +- When multiple dependent tasks share the same predecessor, order them after the dependency marker: `01+01_{task_name}`, `01+02_{task_name}`. +- Directory names are runtime scheduling metadata. Preserve them verbatim; do not normalize, reinterpret, or choose execution order by agent judgment. +- Every split plan must include `의존 관계 및 구현 순서` and list predecessor task directories that must reach `complete.log` before implementation begins. + Routing rules: - Build defaults to `local` when the plan is explicit, tests are runnable, and failure is review-detectable. @@ -57,12 +68,14 @@ Otherwise, glob `agent-task/*/PLAN-*-G??.md`: |--------|--------| | Exactly one | Continue that task | | None | Create a new task only for a feature/refactor/fix/follow-up that belongs in this workflow | -| Multiple | List paths and ask which task to use | +| Multiple | If the user/runtime named a task directory, use that directory. Otherwise list paths and ask which task to use; do not choose by agent judgment. | The routed plan file is the loop entry point. A missing active plan normally means only that no plan has been started for a new task; do not create task files for casual analysis, status, or review requests unless the user explicitly asks for a plan. Use short snake_case task names, e.g. `api_refactor`. +When the work must be split into multiple plans, choose directory names using the task directory naming rules above. Do not put multiple active plan files in one task directory. + ## Step 2 - Analyze Before Writing Complete all items below before creating any files. Work through them in order; do not proceed to the next step until every checkbox is done. @@ -114,7 +127,7 @@ Header line must be exactly: Required sections: - Title. -- `이 파일을 읽는 구현 에이전트에게`: open with a bold warning that filling in `CODE_REVIEW-*-G??.md` is a mandatory final step — the task is NOT complete until every section of that file is filled. Tell the implementer to complete checklists, run intermediate/final verification, and fill every `CODE_REVIEW-*-G??.md` section with actual implementation notes and command output. Explicitly state that the implementing agent must NOT execute the archiving instructions in the review file's `이 파일을 읽는 리뷰 에이전트에게` section — those instructions (renaming to `*.log`, writing `complete.log`) are for the code-review skill only. +- `이 파일을 읽는 구현 에이전트에게`: open with a bold warning that filling in implementation-owned sections of `CODE_REVIEW-*-G??.md` is a mandatory final step — the task is NOT complete until every implementation-owned section of that file is filled. Tell the implementer to work from the implementation checklist, complete every checklist item in both the plan and review stub, run intermediate/final verification, and fill implementation-owned `CODE_REVIEW-*-G??.md` sections with actual implementation notes and command output. Explicitly state that the implementing agent must NOT execute the archiving instructions in the review file's `이 파일을 읽는 리뷰 에이전트에게` section and must NOT modify or check the `코드리뷰 전용 체크리스트` — those instructions/checklists are for the code-review skill only. - `배경`: 2-4 sentences explaining why the work is needed. - `분석 결과`: record the findings from Step 2 and Step 3. This section is the written output of the analysis — not a summary, but the actual findings that justify the plan's scope and decisions. Must include all of the following subsections: - `읽은 파일`: list every source and test file read during analysis, with path. @@ -122,9 +135,10 @@ Required sections: - `심볼 참조`: list renamed/removed symbols and every call site found, or state "none" if no symbols were changed. - `범위 결정 근거`: state which files or areas were explicitly excluded from this change and why. This is the boundary justification — the implementing agent must not silently expand scope beyond what is recorded here. - `빌드 등급`: state the decided lane and GNN grade with a one-line rationale. +- `구현 체크리스트`: a top-level checklist the implementing agent must follow while coding. Include one item per plan item, one item for all intermediate/final verification, and make the final item exactly: `- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.` Copy this checklist into the review stub's `구현 체크리스트` section with the same item text and order. - One item per change: `### [TAG-1] Title`, `TAG-2`, etc. - `수정 파일 요약`: table mapping files to item ids. -- `최종 검증`: runnable commands and expected outcome. Commands must be exact and deterministic enough for the reviewer to rerun; use stable ordering for searches and state whether cached test output is acceptable. The final line of this section must read exactly — **"모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 전체 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다."** +- `최종 검증`: runnable commands and expected outcome. Commands must be exact and deterministic enough for the reviewer to rerun; use stable ordering for searches and state whether cached test output is acceptable. The final line of this section must read exactly — **"모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다."** Each plan item must include: @@ -136,6 +150,8 @@ Each plan item must include: Include `의존 관계 및 구현 순서` only when order matters. +For split multi-plan work, `의존 관계 및 구현 순서` is mandatory in every generated plan. If a plan has a `NN+...` directory name, state which `NN_...` predecessor must produce `complete.log` before implementation starts. + Quality rules: - Exact line numbers in every Before snippet. @@ -177,7 +193,9 @@ Use the template below exactly. Fill `{…}` placeholders from the plan; everyth # Code Review Reference - {TAG} > **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** -> The task is NOT complete until every section below is filled in. +> The task is NOT complete until every implementation-owned section below is filled in. +> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. +> Do not modify or check the `코드리뷰 전용 체크리스트`; it is owned by the review agent only. > Follow the ownership table at the bottom of this file for which sections you own. ## 개요 @@ -194,6 +212,9 @@ task={task_name}, plan={N}, tag={TAG} 2. `PLAN-{build_lane}-GNN.md` → `plan_{build_lane}_GNN_M.log` (M = 기존 plan_*.log 수) 3. PASS인 경우 `complete.log` 작성 후 종료. WARN/FAIL인 경우 새 routed plan + review 스텁 작성. +어떤 판정에서도 아카이브를 건너뛰지 마세요. PASS/WARN/FAIL 모두 `코드리뷰 결과` append 후 active plan/review 파일을 먼저 아카이브하고, 그 다음 `complete.log` 또는 다음 plan/review 파일을 작성해야 합니다. +아카이브와 다음 상태 파일 작성이 끝난 뒤, archived `code_review_*.log`의 `코드리뷰 전용 체크리스트`를 모두 체크한 다음 보고하세요. + --- ## 구현 항목별 완료 여부 @@ -203,6 +224,22 @@ task={task_name}, plan={N}, tag={TAG} | [{TAG}-1] {item description} | [ ] | | [{TAG}-2] {item description} | [ ] | +## 구현 체크리스트 + +{copy the plan's 구현 체크리스트 items exactly, preserving order and checkbox text} + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. +- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] WARN/FAIL이면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. + ## 계획 대비 변경 사항 _구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ @@ -238,8 +275,9 @@ $ {final verification command from plan} --- -> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every section: completion table, changes from plan, design decisions, and verification output?** +> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?** > If anything is blank, go back and fill it in before saving this file. +> Leave the review-agent-only checklist unchanged. ``` Sections and their ownership: @@ -248,6 +286,8 @@ Sections and their ownership: |---------|-------|------| | Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these (archive + complete.log are review-agent only) | | 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only | +| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only; final checkbox is mandatory before saving | +| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section | | 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content | | 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan | | 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry | @@ -265,7 +305,10 @@ Sections and their ownership: ## Final Checklist - `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md` both exist under `agent-task/{task_name}/`. +- Split work, if any, uses one task directory per plan/review pair with `01_...`, `02_...`, or dependency-marked `NN+...` names. - Both first lines match ``. - Previous active files, if any, were archived with correct numeric suffixes. - Every plan item has problem, solution, checklist, test decision, and intermediate verification. +- The plan and review stub have matching `구현 체크리스트` item text/order, and the final checkbox is the mandatory `CODE_REVIEW-*-G??.md` completion item. +- The review stub has a clearly marked `코드리뷰 전용 체크리스트` owned only by the review agent. - Routed review file completion table lists every plan item. diff --git a/agent-task/01+edge_runtime_common/code_review_cloud_G07_0.log b/agent-task/01+edge_runtime_common/code_review_cloud_G07_0.log new file mode 100644 index 0000000..c02aaec --- /dev/null +++ b/agent-task/01+edge_runtime_common/code_review_cloud_G07_0.log @@ -0,0 +1,150 @@ + + +# Code Review Reference - ERC + +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** +> The task is NOT complete until every section below is filled in. + +## 개요 + +date=2026-05-17 +task=01_edge_runtime_common, plan=0, tag=ERC + +## 이 파일을 읽는 리뷰 에이전트에게 + +실제 소스와 검증 출력을 대조하세요. PASS 시 `CODE_REVIEW-cloud-G07.md`, `PLAN-cloud-G07.md`를 log로 아카이브하고 `complete.log`를 작성하세요. + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|---|---| +| [ERC-1] 공통 runtime builder 추가 | [x] | +| [ERC-2] serve/console이 공통 runtime 사용 | [x] | + +## 계획 대비 변경 사항 + +- 계획에 없던 `transport.Server.HasRunEventHandler()`, `HasNodeEventHandler()` getter 두 개를 추가했다. `bootstrap` 테스트에서 server의 private handler 필드를 검사하기 위한 최소한의 introspection 표면이다. 기존 SetXxxHandler 패턴과 대칭이며 production 코드는 사용하지 않는다. +- 계획에 명시되지 않았던 `TestNewRuntimeRejectsDuplicateToken`을 추가했다. `NewRuntime`이 `LoadFromConfig`의 검증 실패를 그대로 surface하는지 확인하기 위한 보조 케이스다. +- `runtime.wireHandlers()`를 별도 메서드로 분리했다. `Start`가 서버 바인딩까지 수행하므로 핸들러 wiring만 단위 테스트할 수 있도록 분리한 것이다. + +## 주요 설계 결정 + +- `Runtime`은 `Cfg`, `Logger`, `Registry`, `NodeStore`, `EventBus`, `Service`, `Server` 필드를 공개로 노출한다. console loop가 service/registry/event bus를 직접 참조해야 하기 때문에 캡슐화보다 접근 편의를 택했다. 다른 패키지에서는 `bootstrap.Module` 또는 `bootstrap.NewRuntime`만 사용한다. +- `NewRuntime`이 `LoadFromConfig`를 직접 호출하도록 했다. 기존 `Module`은 fx provider에서 빈 `NodeStore`를 만들고 OnStart에서 seed 했지만, console 경로와 동일하게 만들기 위해 생성 시점에 seed하도록 통합했다. seed 결과 자체가 의존성이므로 fx provider 안에서 실패하는 편이 lifecycle 도중 실패보다 진단이 쉽다. +- metrics 서버 goroutine 시작은 `Start` 안에 남겼다. plan의 "Start에서 metrics start" 요구와 기존 동작(blocking ListenAndServe를 goroutine으로 띄움)을 유지한다. +- `Stop`은 `Server.Stop()` 후 `Logger.Sync()`를 호출한다. 기존 console의 `defer logger.Sync()` 효과를 runtime 종료 책임으로 흡수했다. +- `bootstrap.Module`은 개별 컴포넌트 fx.Provide를 제거하고 `NewRuntime`만 provide한다. 외부 사용처가 `bootstrap.Module(cfg)` 하나뿐이라 broader DI surface는 불필요하다. + +## 리뷰어를 위한 체크포인트 + +- Runtime 조립이 serve/console에 중복되지 않는가. + - `console.go`는 `bootstrap.NewRuntime(cfg)` + `rt.Start(ctx)` + `defer rt.Stop()`만 호출한다. `module.go`는 `fx.Provide(NewRuntime)`과 fx.Lifecycle에서 동일한 `rt.Start`/`rt.Stop`을 호출한다. 조립 코드는 `runtime.go`에만 존재한다. +- NodeStore seed와 event handler wiring 순서가 기존과 동일한가. + - seed는 `NewRuntime` 시점, handler wiring은 `Start` 첫 단계(`wireHandlers`)에서 수행되며 그 뒤에 `Server.Start` → metrics goroutine이 이어진다. 기존 console 흐름(seed → wiring → start → metrics)과 module 흐름(seed via Load → wiring → start → metrics) 모두 동일 순서로 통합되었다. +- Metrics/server lifecycle이 테스트에서 실제 포트 충돌 없이 검증되는가. + - 테스트는 listen `127.0.0.1:0`, metrics port `0`을 사용한다. `Start`를 호출하는 케이스는 없고, `wireHandlers`만 직접 호출해 wiring을 검증한다. 따라서 실제 포트 바인딩과 metrics goroutine은 테스트에서 기동되지 않는다. + +## 검증 결과 + +``` +$ go test -count=1 ./apps/edge/internal/bootstrap +ok iop/apps/edge/internal/bootstrap 0.005s + +$ go test -count=1 ./apps/edge/internal/bootstrap ./apps/edge/cmd/edge +ok iop/apps/edge/internal/bootstrap 0.005s +ok iop/apps/edge/cmd/edge 0.005s + +$ go test -count=1 ./... +? iop/apps/control-plane/cmd/control-plane [no test files] +ok iop/apps/edge/cmd/edge 0.006s +ok iop/apps/edge/internal/bootstrap 0.006s +ok iop/apps/edge/internal/events 0.007s +ok iop/apps/edge/internal/node 0.004s +ok iop/apps/edge/internal/service 0.004s +ok iop/apps/edge/internal/transport 0.012s +? iop/apps/node/cmd/node [no test files] +ok iop/apps/node/internal/adapters 0.008s +ok iop/apps/node/internal/adapters/cli 87.685s +? iop/apps/node/internal/adapters/cli/internal/testutil [no test files] +ok iop/apps/node/internal/adapters/cli/status 30.979s +? iop/apps/node/internal/adapters/mock [no test files] +? iop/apps/node/internal/adapters/ollama [no test files] +? iop/apps/node/internal/adapters/vllm [no test files] +ok iop/apps/node/internal/bootstrap 0.163s +ok iop/apps/node/internal/node 0.013s +ok iop/apps/node/internal/router 0.008s +? iop/apps/node/internal/runtime [no test files] +ok iop/apps/node/internal/store 0.030s +ok iop/apps/node/internal/transport 0.008s +? iop/apps/worker/cmd/worker [no test files] +? iop/packages/auth [no test files] +ok iop/packages/config 0.012s +? iop/packages/events [no test files] +? iop/packages/jobs [no test files] +? iop/packages/metadata [no test files] +? iop/packages/observability [no test files] +? iop/packages/policy [no test files] +? iop/packages/version [no test files] +? iop/proto/gen/iop [no test files] +``` + +--- + +## 코드리뷰 결과 + +### 종합 판정 + +PASS + +### 차원별 평가 + +| 차원 | 평가 | 메모 | +|---|---|---| +| correctness | Pass | `serve`와 `console`이 `bootstrap.NewRuntime` 기반 조립을 공유하며, seed/wiring/server/metrics 순서가 기존 흐름과 일치한다. | +| completeness | Pass | 계획된 공통 runtime builder, fx module 전환, console 전환, runtime seed/wiring 테스트가 구현되어 있다. | +| test coverage | Pass | `NewRuntime` seed, duplicate token error surface, handler wiring 단위 테스트가 추가되었고 기존 console 테스트가 유지된다. | +| API contract | Pass | transport protocol/protobuf 변경 없이 `transport.Server` handler getter만 테스트 introspection 용도로 추가되었다. | +| code quality | Pass | 중복 조립 코드가 제거되고 lifecycle 책임이 `Runtime`에 모였다. `gofmt -l` 및 `git diff --check` clean. | +| plan deviation | Pass | 계획 대비 변경 사항은 review 문서에 기록되어 있고 범위를 벗어난 production behavior 변경은 보이지 않는다. | +| verification trust | Pass | 구현 에이전트가 기록한 Go 테스트를 재현했고, 리뷰어가 추가로 `bin/edge.sh` + `bin/node.sh` mock E2E smoke를 실행했다. | + +### 발견된 문제 + +없음 + +### 리뷰어 추가 검증 + +```bash +$ go test -count=1 ./apps/edge/internal/bootstrap ./apps/edge/cmd/edge +ok iop/apps/edge/internal/bootstrap 0.004s +ok iop/apps/edge/cmd/edge 0.004s + +$ go test -count=1 ./... +ok iop/apps/edge/cmd/edge 0.007s +ok iop/apps/edge/internal/bootstrap 0.007s +ok iop/apps/edge/internal/events 0.005s +ok iop/apps/edge/internal/node 0.005s +ok iop/apps/edge/internal/service 0.007s +ok iop/apps/edge/internal/transport 0.012s +ok iop/apps/node/internal/adapters/cli 87.706s +ok iop/apps/node/internal/adapters/cli/status 31.070s +ok iop/apps/node/internal/bootstrap 0.165s +ok iop/packages/config 0.007s +# 나머지는 [no test files] 또는 위 출력과 동일한 pass 패키지 + +$ gofmt -l apps/edge/internal/bootstrap/runtime.go apps/edge/internal/bootstrap/runtime_test.go apps/edge/internal/bootstrap/module.go apps/edge/cmd/edge/console.go apps/edge/internal/transport/server.go +# no output + +$ git diff --check -- apps/edge/cmd/edge/console.go apps/edge/internal/bootstrap/module.go apps/edge/internal/transport/server.go apps/edge/internal/bootstrap/runtime.go apps/edge/internal/bootstrap/runtime_test.go +# no output +``` + +Mock E2E smoke: + +- 임시 edge/node config와 랜덤 포트를 사용해 `bin/edge.sh`, `bin/node.sh` 실제 entrypoint를 실행했다. +- 확인 항목: node register, `/nodes`, foreground message 2회, session 변경 후 run, background run, `/terminate-session`, `/status`, `/exit`. +- 결과: PASS. `mock` adapter status는 명확한 unsupported error(`node: adapter "mock" does not support commands`)를 반환했다. + +### 다음 단계 + +PASS: `CODE_REVIEW-cloud-G07.md`, `PLAN-cloud-G07.md`를 log로 아카이브하고 `complete.log`를 작성한다. diff --git a/agent-task/01+edge_runtime_common/complete.log b/agent-task/01+edge_runtime_common/complete.log new file mode 100644 index 0000000..bfce2f6 --- /dev/null +++ b/agent-task/01+edge_runtime_common/complete.log @@ -0,0 +1,16 @@ +완료 일시: 2026-05-17 + +요약: Edge serve/console 공통 runtime 조립 경로 추가 및 적용을 1개 루프로 PASS 완료. + +루프 이력: + +| Plan | Code Review | Verdict | +|---|---|---| +| `plan_cloud_G07_0.log` | `code_review_cloud_G07_0.log` | PASS | + +최종 리뷰 요약: + +- `apps/edge/internal/bootstrap.Runtime`와 `NewRuntime`이 logger, registry, node store, event bus, service, transport server 조립을 공통화했다. +- `bootstrap.Module`과 `runConsole`이 같은 runtime의 `Start`/`Stop` lifecycle을 사용하도록 정리되었다. +- runtime seed, duplicate token error, transport handler wiring 검증 테스트가 추가되었다. +- 리뷰어 재현 검증: 대상 패키지 테스트, `go test -count=1 ./...`, 포맷/whitespace 검사, `bin/edge.sh` + `bin/node.sh` mock E2E smoke 모두 PASS. diff --git a/agent-task/01+edge_runtime_common/plan_cloud_G07_0.log b/agent-task/01+edge_runtime_common/plan_cloud_G07_0.log new file mode 100644 index 0000000..ce7e0b8 --- /dev/null +++ b/agent-task/01+edge_runtime_common/plan_cloud_G07_0.log @@ -0,0 +1,129 @@ + + +# 01 Edge Runtime Common Plan - ERC + +## 이 파일을 읽는 구현 에이전트에게 + +**필수: 구현 마지막에는 반드시 `CODE_REVIEW-*-G??.md`의 모든 섹션을 실제 구현 내용과 명령 출력으로 채운다.** +중간/최종 검증을 실행하고 `CODE_REVIEW-cloud-G07.md`를 채운다. 구현 에이전트는 review 파일의 아카이브 지시를 실행하지 않는다. + +## 배경 + +Edge console을 ops/debug 표면으로 유지하려면 `serve`와 `console`이 같은 runtime 조립 경로를 써야 한다. 지금은 console이 logger, registry, node store, transport server, event bus, metrics를 직접 조립하고, serve는 fx module에서 별도 조립한다. + +## 분석 결과 + +### 읽은 파일 + +- `agent-ops/skills/common/plan/SKILL.md` +- `agent-ops/rules/project/domain/edge/rules.md` +- `agent-ops/rules/project/domain/testing/rules.md` +- `apps/edge/cmd/edge/main.go` +- `apps/edge/cmd/edge/console.go` +- `apps/edge/cmd/edge/console_test.go` +- `apps/edge/internal/bootstrap/module.go` +- `apps/edge/internal/transport/server.go` +- `apps/edge/internal/node/store.go` +- `apps/edge/internal/service/service.go` +- `apps/edge/internal/events/bus.go` +- `packages/config/config.go` + +### 테스트 커버리지 공백 + +- 공통 runtime 생성/seed/handler wiring을 검증하는 테스트가 없다. +- console tests는 출력 helper 중심이라 runtime 중복 제거를 보장하지 않는다. + +### 심볼 참조 + +- `runConsole`: `apps/edge/cmd/edge/console.go:23`, 호출 `apps/edge/cmd/edge/main.go:58`. +- `bootstrap.Module`: `apps/edge/internal/bootstrap/module.go:18`, 호출 `apps/edge/cmd/edge/main.go:43`. + +### 범위 결정 근거 + +- Control Plane API 구현은 제외한다. +- transport 프로토콜 변경은 제외한다. +- Node adapter 실행 로직은 제외한다. + +### 빌드 등급 + +- Build `cloud-G07`: edge bootstrap/cmd 경계와 bin-level smoke 영향이 있다. +- Review `cloud-G07`: runtime wiring과 실제 entrypoint 검증 증거가 필요하다. + +### [ERC-1] 공통 runtime builder 추가 + +#### 문제 + +`console.go:23-63`과 `module.go:18-75`가 edge runtime 조립을 나눠 가진다. + +#### 해결 방법 + +- `apps/edge/internal/bootstrap/runtime.go`에 `Runtime` 구조체와 `NewRuntime(cfg)`를 추가한다. +- `Runtime`은 `Logger`, `Registry`, `NodeStore`, `EventBus`, `Service`, `Server`를 보유한다. +- `Start(ctx)`에서 node store seed, event handler wiring, server start, metrics start를 처리한다. +- `Stop()`에서 server stop과 logger sync를 처리한다. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/edge/internal/bootstrap/runtime.go` +- [ ] `apps/edge/internal/bootstrap/runtime_test.go` +- [ ] `apps/edge/internal/bootstrap/module.go` + +#### 테스트 작성 + +- `TestNewRuntimeSeedsNodeStore` +- `TestRuntimeWiresTransportHandlers` + +#### 중간 검증 + +```bash +go test -count=1 ./apps/edge/internal/bootstrap +``` + +### [ERC-2] serve/console이 공통 runtime 사용 + +#### 문제 + +`runConsole`이 직접 조립해서 serve와 lifecycle 차이가 생긴다. + +#### 해결 방법 + +- `serve` path는 `bootstrap.Module` 내부에서 `Runtime`을 사용한다. +- `console` path는 `bootstrap.NewRuntime(cfg)` 후 `Start/Stop`을 호출한다. +- console loop는 runtime의 `Service`, `Registry`, `EventBus`만 참조한다. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/edge/cmd/edge/console.go` +- [ ] `apps/edge/cmd/edge/main.go` +- [ ] `apps/edge/cmd/edge/console_test.go` + +#### 테스트 작성 + +- 기존 console tests 유지. +- 필요 시 fake runtime을 주입할 수 있게 console loop를 분리한다. + +#### 중간 검증 + +```bash +go test -count=1 ./apps/edge/cmd/edge ./apps/edge/internal/bootstrap +``` + +## 수정 파일 요약 + +| 파일 | 항목 | +|---|---| +| `apps/edge/internal/bootstrap/runtime.go` | ERC-1 | +| `apps/edge/internal/bootstrap/runtime_test.go` | ERC-1 | +| `apps/edge/internal/bootstrap/module.go` | ERC-1, ERC-2 | +| `apps/edge/cmd/edge/console.go` | ERC-2 | +| `apps/edge/cmd/edge/main.go` | ERC-2 | +| `apps/edge/cmd/edge/console_test.go` | ERC-2 | + +## 최종 검증 + +```bash +go test -count=1 ./apps/edge/internal/bootstrap ./apps/edge/cmd/edge +go test -count=1 ./... +``` + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 전체 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/02+edge_opsconsole_package/code_review_cloud_G07_0.log b/agent-task/02+edge_opsconsole_package/code_review_cloud_G07_0.log new file mode 100644 index 0000000..8329ea1 --- /dev/null +++ b/agent-task/02+edge_opsconsole_package/code_review_cloud_G07_0.log @@ -0,0 +1,167 @@ + + +# Code Review Reference - EOP + +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** + +## 개요 + +date=2026-05-17 +task=02_edge_opsconsole_package, plan=0, tag=EOP + +## 이 파일을 읽는 리뷰 에이전트에게 + +구현과 검증 출력을 대조하고 PASS 시 plan/review를 log로 아카이브하세요. + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|---|---| +| [EOP-1] opsconsole 패키지 생성 | [x] | +| [EOP-2] event/status renderer 이동 | [x] | + +## 계획 대비 변경 사항 + +- 계획에 명시한 4개 신규 파일 (`opsconsole/console.go`, `console_test.go`, `events.go`, `events_test.go`)에 더해 `opsconsole/status.go`를 분리 생성했다. plan 표에 `status.go`가 EOP-2 산출물로 이미 들어 있어 범위 추가는 없다. +- `cmd/edge/console_events.go`는 wrapper로 축소하지 않고 완전히 삭제했다. cmd 측에서 더 이상 참조하지 않고, `runConsole`이 `opsconsole.Run`을 직접 호출하기 때문이다. +- `cmd/edge/console_test.go`도 전부 제거하고 opsconsole 패키지 테스트로 이동했다. 일부(BuildRunRequest 변형, ResolveNode 다중/단일 노드 케이스)는 서비스 계층 책임이라 `internal/service/service_test.go`에 추가했다. +- parser 시그니처에서 bare `/node`, `/session`, `/background`(인자 없는 형태)도 `UsageErr`로 분류했다. 기존 코드에서는 `strings.HasPrefix(lower, "/node ")` 조건이 `bufio.Scanner` 결과의 trailing space 처리 때문에 사실상 unreachable이었으나, parser test 의도(사용법 안내)를 살리도록 분기 보강. + +## 주요 설계 결정 + +- **얇은 entrypoint**: `cmd/edge/console.go`는 `opsconsole.Run(ctx, cfg, in, out)` 한 줄 위임으로 단순화. cobra 명령은 `main.go`에만 남는다. +- **공개 API 표면**: `Run`, `TargetState`, `Command`/`CommandKind`/`ParseCommand`, `EventRouter`/`NewEventRouter`, `ResponseStream`/`NewResponseStream`, `FormatUsageStatus`, `SendStatus`, `SendRun`, `HandleTerminateSession`, `SendTerminateSessionFunc`, `PrintNodes`, `NormalizeSessionID`, `BuildNodeCommandRequest`, `StatusWaitTimeout`만 노출. 내부 헬퍼(`transportCloseDetail`, `usageStatusLabel` 등)는 패키지 내부에 유지. +- **parser 분리**: command loop에 직접 박혀 있던 `switch ... case strings.HasPrefix` 더미를 `ParseCommand(line) Command` 단일 함수로 분리해 table test가 가능하도록 했다. +- **출력 호환성**: 모든 prefix(`[edge] sent ...`, `[node-