diff --git a/agent-ops/rules/project/domain/client/rules.md b/agent-ops/rules/project/domain/client/rules.md new file mode 100644 index 0000000..28927d4 --- /dev/null +++ b/agent-ops/rules/project/domain/client/rules.md @@ -0,0 +1,74 @@ +--- +domain: client +last_rule_review_commit: aaf35e61a91d33b81b64b1054649f7e28ad421c0 +last_rule_updated_at: 2026-05-31 +--- + +# client + +## 목적 / 책임 + +IOP의 공식 Flutter client UI/UX 영역이다. Control Plane HTTP/WS endpoint를 소비해 운영 상태를 표시하고, Flutter Web/Desktop/Mobile shell, Client-Control Plane proto-socket client, client-side integration host를 관리한다. + +## 포함 경로 + +- `apps/client/lib/` — Flutter app source, build-time client config, proto-socket wire client, generated Dart proto 사용, client integration code +- `apps/client/test/` — Flutter widget/config/wire/integration 테스트 +- `apps/client/web/` — Flutter Web shell과 web asset +- `apps/client/android/` — Android runner와 Firebase 설정 표면 +- `apps/client/ios/` — iOS runner scaffold +- `apps/client/macos/` — macOS runner scaffold +- `apps/client/linux/` — Linux runner scaffold +- `apps/client/windows/` — Windows runner scaffold +- `apps/client/pubspec.yaml` — Flutter dependency와 asset 설정 +- `apps/client/pubspec.lock` — Flutter dependency lock +- `apps/client/analysis_options.yaml` — Dart/Flutter lint 설정 +- `apps/client/Dockerfile` — Flutter Web build와 nginx static serving 이미지 +- `apps/client/nginx.conf` — Flutter Web 정적 서빙 설정 +- `apps/client/README.md` — client 개발, 검증, build-time 설정 안내 + +## 제외 경로 + +- `apps/control-plane/` — Client가 연결하는 Control Plane server와 wire endpoint 구현 영역 +- `apps/edge/` — Edge-local 실행 그룹과 node registry/operation 소유자 +- `apps/node/` — adapter execution과 runtime session 소유자 +- `packages/` — Go 공통 설정/관측성/정책 helper 영역 +- `proto/iop/` — protobuf 계약 원본 +- `proto/gen/iop/` — Go protobuf 생성물 +- `scripts/dev/` — repo 내부 개발 실행 helper 영역 +- `docker-compose.yml` — local dev stack 조립과 검증 환경 영역 + +## 주요 구성 요소 + +- `IopClientApp` / `ClientHomePage` — IOP client Flutter app shell과 현재 wire status UI +- `ClientConfig` — `--dart-define` 기반 Control Plane HTTP/WS URL 설정 +- `ClientWireClient` — Control Plane `/client` WebSocket proto-socket client와 hello request helper +- `clientParserMap` — Client-Control Plane proto message parser map +- `apps/client/lib/gen/proto/iop/*.dart` — `make proto-dart`로 생성되는 Dart protobuf binding +- `MattermostPushHostIntegration` / `MattermostPushPluginClient` — Nexo messaging / Mattermost-compatible push integration host +- `apps/client/Dockerfile` — sibling `proto-socket/dart` path dependency를 포함해 Flutter Web artifact를 빌드하는 이미지 + +## 유지할 패턴 + +- Client는 Control Plane을 통해 Edge/Node 운영 상태를 조회한다. Client에서 Edge-Node TCP transport나 Node execution 경로에 직접 붙지 않는다. +- Client-Control Plane native 경계는 `/client` WebSocket proto-socket과 `ClientHelloRequest`/`ClientHelloResponse` baseline을 따른다. +- Control Plane URL은 `IOP_CONTROL_PLANE_HTTP_URL`, `IOP_CONTROL_PLANE_WIRE_URL` `--dart-define`으로 주입한다. 실제 환경값이나 private endpoint를 tracked source에 고정하지 않는다. +- Dart protobuf binding은 `proto/iop/*.proto`에서 생성한다. proto 계약 변경 시 `make proto-dart` 산출물과 Go 생성물 갱신 여부를 함께 확인한다. +- `apps/client/lib/gen/proto/iop/*.dart` 생성물은 사람이 직접 수정하지 않는다. +- Mattermost/Nexo 연동은 `lib/src/integrations/` 아래 통합 모듈로 둔다. IOP client app shell을 NomadCode 전용 UX로 바꾸지 않는다. +- client 변경 후에는 변경 범위에 맞게 `flutter test` 또는 `make client-test`를 확인한다. Web build, Dockerfile, compose 경로를 바꾸면 `make client-build-web` 또는 해당 build 경로를 확인한다. + +## 다른 도메인과의 경계 + +- **control-plane**: Control Plane은 `/client` WS endpoint, HTTP 상태 endpoint, Edge connection registry를 제공한다. Client는 이를 소비하는 UI/UX와 client-side wire wrapper를 소유한다. +- **platform-common**: protobuf 원본 계약은 platform-common이 소유한다. Client는 해당 계약에서 생성된 Dart binding을 사용한다. +- **testing**: client test/build target, `scripts/dev/web.sh`, compose 기반 local dev 검증 기준은 testing domain rule을 따른다. +- **edge/node**: Client는 Edge와 Node를 직접 제어하지 않고 Control Plane 표면을 통해 관찰/명령한다. + +## 금지 사항 + +- Client에서 Edge 또는 Node TCP/protobuf 내부 transport에 직접 연결하지 않는다. +- Client를 NomadCode 전용 shell, project workspace, diff/PR/branch/commit UX 중심으로 만들지 않는다. +- `apps/client/lib/gen/proto/iop/*.dart` 생성 파일을 직접 수정하지 않는다. +- Mattermost smoke credential 같은 local/private 값을 tracked asset이나 README에 기록하지 않는다. +- Control Plane server 상태 저장, Edge registry, run dispatch, stream relay 구현을 client domain으로 끌어오지 않는다. diff --git a/agent-ops/rules/project/domain/control-plane/rules.md b/agent-ops/rules/project/domain/control-plane/rules.md index e02d662..4303bde 100644 --- a/agent-ops/rules/project/domain/control-plane/rules.md +++ b/agent-ops/rules/project/domain/control-plane/rules.md @@ -1,25 +1,26 @@ --- domain: control-plane -last_rule_review_commit: 7b9345bc1af3d7c449e07b6fc9627d984c80cebf -last_rule_updated_at: 2026-05-30 +last_rule_review_commit: aaf35e61a91d33b81b64b1054649f7e28ad421c0 +last_rule_updated_at: 2026-05-31 --- # control-plane ## 목적 / 책임 -여러 Edge를 관찰하고 운영 제어할 중앙 제어면 영역이다. 현재는 health/readiness HTTP, 환경별 설정 로딩, proto-socket WebSocket hello baseline을 제공하며, Node 직접 연결이나 직접 스케줄링은 책임으로 삼지 않는다. +여러 Edge를 관찰하고 운영 제어할 중앙 제어면 영역이다. 현재는 health/readiness와 Edge registry 조회 HTTP, 환경별 설정 로딩, Client proto-socket WebSocket, Edge proto-socket TCP 연결 baseline을 제공하며, Node 직접 연결이나 직접 스케줄링은 책임으로 삼지 않는다. ## 포함 경로 -- `apps/control-plane/cmd/control-plane/` — control-plane CLI 진입점, 설정 로딩, HTTP health/readiness 서버, wire server lifecycle -- `apps/control-plane/internal/wire/` — Control Plane wire protocol 상수와 WebSocket proto-socket endpoint +- `apps/control-plane/cmd/control-plane/` — control-plane CLI 진입점, 설정 로딩, HTTP health/readiness와 Edge registry 조회 endpoint, wire server lifecycle +- `apps/control-plane/internal/wire/` — Control Plane wire protocol 상수, Client WebSocket endpoint, Edge TCP server와 in-memory Edge registry - `apps/control-plane/README.md` — Control Plane 현재 상태와 계획된 운영 책임 설명 - `apps/control-plane/Dockerfile` — Control Plane 배포용 이미지 빌드 표면 ## 제외 경로 - `apps/edge/` — Edge-local Node registry, routing, input surface, stream relay의 소유자 +- `apps/client/` — Control Plane을 소비하는 Flutter client UI/UX 영역 - `apps/node/` — 실제 adapter execution과 runtime session의 소유자 - `apps/worker/` — 비동기 작업 처리 예정 영역 - `packages/` — 여러 앱이 공유하는 공통 패키지 @@ -29,20 +30,28 @@ last_rule_updated_at: 2026-05-30 ## 주요 구성 요소 - `rootCmd()` / `serveCmd()` — `control-plane serve` command와 기본 config path 구성 -- `controlPlaneConfig` — HTTP listen, wire listen, DB, Redis, logging 설정 구조 +- `controlPlaneConfig` — HTTP listen, Client wire listen, Edge wire listen, DB, Redis, logging 설정 구조 - `loadConfig()` / `applyEnvOverrides()` — YAML 설정 로딩과 `IOP_*` 환경 변수 override -- `run()` — health/readiness HTTP server와 WebSocket wire server lifecycle 조립 +- `run()` — health/readiness/Edge registry HTTP server, Client WS server, Edge TCP server lifecycle 조립 +- `registerEdgeRegistryHandlers()` — `/edges`, `/edges/{edge_id}`, `/edges/{edge_id}/events` JSON 조회 endpoint - `wire.Protocol` — Control Plane 통신 표준을 `protobuf-socket`으로 고정하는 상수 - `wire.Endpoint` — reserved wire endpoint 설정 타입 -- wire WebSocket server — proto-socket hello 요청을 처리하는 서버 구현 +- `wire.ClientServer` — `/client` WebSocket proto-socket hello 요청을 처리하는 서버 구현 +- `wire.EdgeServer` — Edge outbound TCP proto-socket 연결을 수락하고 hello/status/event 흐름을 처리하는 서버 구현 +- `wire.EdgeRegistry` — Edge connection state와 최근 node lifecycle event를 보관하는 in-memory registry +- `wire.EdgeParserMap()` — Control Plane-Edge wire message parser map +- `wire.EdgeServer.RequestStatus()` — 연결된 Edge에 `EdgeStatusRequest`를 보내 Edge-owned node snapshot을 조회하는 helper ## 유지할 패턴 - Control Plane은 Edge를 통해 시스템을 관찰/제어한다. Node를 직접 등록하거나 직접 스케줄링하는 active contract를 만들지 않는다. - Control Plane은 상시 운영, multi-edge 관찰/명령, 정책/감사 UX의 기본 운영면으로 확장한다. Edge-local bootstrap, config, 진단, 단일 Edge 유지보수 fallback을 대체하거나 제거하는 전제로 설계하지 않는다. - Control Plane-Edge wire protocol은 Edge가 소유한 operation contract를 원격 운영면으로 소비하는 형태로 설계한다. Edge CLI와 같은 행위를 노출해야 하면 Edge 쪽 표면 중립 service/operation boundary로 수렴시킨다. -- `net/http`는 health/readiness/bootstrap 같은 보조 endpoint에 우선 사용하고, IOP 고유 운영 통신은 proto-socket 기반 wire protocol로 확장한다. +- `net/http`는 health/readiness와 단순 조회성 status endpoint에 우선 사용하고, IOP 고유 양방향 운영 통신은 proto-socket 기반 wire protocol로 확장한다. - 브라우저/앱 경계는 WebSocket/WSS proto-socket을 사용할 수 있다. Edge-Node TCP transport 기준을 이 경계에 억지로 적용하지 않는다. +- Control Plane-Edge native wire는 Edge가 outbound로 연결하는 TCP proto-socket 경계로 둔다. Client WebSocket listener와 Edge TCP listener를 혼동하지 않는다. +- Edge registry는 현재 in-memory 관찰 baseline이다. durable history, audit, 정책 저장소를 이 registry에 섞지 않는다. +- Edge status 조회는 Edge가 보고한 `EdgeStatusResponse`를 관찰한다. Control Plane에서 Node address, token, transport internals를 직접 소유하지 않는다. - `ScheduleRequest`/`ScheduleResponse`는 legacy placeholder로만 취급하고, 새 orchestration 계약은 Edge-owned runtime state를 우회하지 않도록 다시 설계한다. - Control Plane 설정에 실제 환경값, credential, private endpoint를 tracked 문서에 쓰지 않는다. 환경별 값은 private rule 라우팅을 따른다. - proto 계약을 바꾸면 `proto/iop/*.proto`를 먼저 수정하고 Go 생성물을 명령으로 갱신한다. @@ -50,6 +59,7 @@ last_rule_updated_at: 2026-05-30 ## 다른 도메인과의 경계 - **edge**: Edge는 로컬 실행 그룹 상태와 Node registry를 소유한다. Control Plane은 Edge를 통해 상태 조회, 설정 변경, 명령 전달, 이벤트 수신을 확장한다. Control Plane 없는 bootstrap/local/field/진단 fallback은 `iop-edge` command 표면에 남긴다. +- **client**: Client는 Control Plane의 HTTP/WS 표면을 소비하는 UI/UX와 Dart wire wrapper를 소유한다. Control Plane은 server endpoint와 Edge connection registry를 소유한다. - **platform-common**: protobuf 원본, 생성물, 공통 설정/관측성 helper는 platform-common 계약을 따른다. - **testing**: Control Plane command, wire endpoint, bootstrap/enrollment 사용자 흐름을 바꾸면 testing domain rule의 검증 기준을 따른다. @@ -57,6 +67,7 @@ last_rule_updated_at: 2026-05-30 - Control Plane에서 Node를 직접 연결하거나 직접 스케줄링하는 경로를 제품 기본 계약으로 만들지 않는다. - Edge-local registry, run dispatch, stream relay 구현을 Control Plane으로 복제하지 않는다. +- Edge가 보고하지 않은 Node address/token/scheduling 필드를 Control Plane status API에 만들지 않는다. - Control Plane 도입만을 이유로 Edge-local CLI bootstrap/config/진단 fallback을 제품 기본 계약에서 제외하거나 제거하지 않는다. 축소는 별도 roadmap 결정과 대체 fallback 기준이 있을 때만 다룬다. - gRPC를 Control Plane 기본 wire protocol로 도입하지 않는다. - 실제 DB/Redis credential이나 환경별 endpoint를 tracked docs/rules에 기록하지 않는다. diff --git a/agent-ops/rules/project/domain/edge/rules.md b/agent-ops/rules/project/domain/edge/rules.md index 407ab14..905f258 100644 --- a/agent-ops/rules/project/domain/edge/rules.md +++ b/agent-ops/rules/project/domain/edge/rules.md @@ -1,19 +1,20 @@ --- domain: edge -last_rule_review_commit: 7b9345bc1af3d7c449e07b6fc9627d984c80cebf -last_rule_updated_at: 2026-05-30 +last_rule_review_commit: aaf35e61a91d33b81b64b1054649f7e28ad421c0 +last_rule_updated_at: 2026-05-31 --- # edge ## 목적 / 책임 -여러 Node를 하나의 로컬 실행 그룹으로 묶는 백엔드 실행 그룹 컨트롤러 영역이다. node 연결을 수락하고 token 기반 등록을 검증한 뒤 adapter/runtime 설정을 내려주며, `iop-edge` command 중심의 local/field 운영 UX, ops console, OpenAI-compatible HTTP, A2A JSON-RPC 입력을 내부 `adapter + target` 실행 요청으로 수렴시킨다. +여러 Node를 하나의 로컬 실행 그룹으로 묶는 백엔드 실행 그룹 컨트롤러 영역이다. node 연결을 수락하고 token 기반 등록을 검증한 뒤 adapter/runtime 설정을 내려주며, `iop-edge` command 중심의 local/field 운영 UX, ops console, OpenAI-compatible HTTP, A2A JSON-RPC, Control Plane outbound connector를 내부 Edge-owned operation으로 수렴시킨다. ## 포함 경로 - `apps/edge/cmd/edge/` — edge CLI 진입점과 serve/console/config/env/setup/node/nodes/smoke 커맨드 - `apps/edge/internal/bootstrap/` — fx 의존성 주입과 서버 시작/종료 lifecycle +- `apps/edge/internal/controlplane/` — Edge에서 Control Plane으로 outbound 연결하는 proto-socket connector - `apps/edge/internal/events/` — run/node event in-process fanout bus - `apps/edge/internal/input/` — OpenAI-compatible/A2A 입력 서버 lifecycle 관리 - `apps/edge/internal/node/` — 연결된 node registry와 node 선택 @@ -27,6 +28,7 @@ last_rule_updated_at: 2026-05-30 - `apps/node/` — 실제 모델 실행과 adapter 관리 - `apps/control-plane/` — 여러 Edge 연결 관리와 운영 제어 API 제공 영역 +- `apps/client/` — Control Plane을 소비하는 Flutter client UI/UX 영역 - `apps/worker/` — 비동기 작업 처리 예정 영역 - `packages/` — 공통 설정/관측성/인증 패키지 - `proto/` — 메시지 계약 원본과 생성물 @@ -46,9 +48,13 @@ last_rule_updated_at: 2026-05-30 - `nodes list` — 설정된 node record를 offline/configured 상태로 조회하는 command - `smoke openai` — `/healthz`, `/v1/models`, `/v1/chat/completions`를 확인하는 OpenAI-compatible 진단 command - `bootstrap.Module` — fx lifecycle에 `bootstrap.Runtime` start/stop을 연결 -- `bootstrap.Runtime` — logger, registry, node store, event bus, service, transport server, input manager를 묶는 실행 조립체 +- `bootstrap.Runtime` — logger, registry, node store, event bus, service, transport server, input manager, artifact server, Control Plane connector를 묶는 실행 조립체 +- `bootstrap.ArtifactServer` — Node bootstrap artifact를 제공하는 Edge-local HTTP server +- `controlplane.Connector` — Control Plane TCP wire에 outbound로 연결하고 hello/status/event relay를 처리하는 connector +- `controlplane.StatusProvider` — Control Plane status request에 답할 Edge-owned node snapshot provider boundary - `events.Bus` — `RunEvent`와 `EdgeNodeEvent` subscriber fanout 및 bounded replay - `service.Service` — node 선택, run dispatch, cancel/terminate-session, node command 요청을 표면 중립 DTO로 제공 +- `service.NodeSnapshot` — Control Plane status response에 쓰는 Edge-owned node snapshot DTO - `service.RunHandle` — foreground run event stream과 dispatch metadata를 함께 들고 있는 handle - `service.NodeCommandView` / `UsageStatusView` — console/HTTP/RPC surface가 공유할 수 있는 node command 결과 DTO - `input.Manager` — OpenAI-compatible 서버와 A2A 서버 lifecycle 소유자 @@ -77,15 +83,18 @@ last_rule_updated_at: 2026-05-30 - OpenAI/Ollama passthrough성 옵션은 입력 표면에서 명시적으로 변환하고, node adapter의 Ollama 실행 계약을 우회하지 않는다. - run/node event fanout은 `events.Bus`를 통해 수행하고, transport handler에서 console/HTTP 표면으로 직접 출력하거나 응답하지 않는다. - 입력 서버 lifecycle은 `input.Manager`가 소유하며, bootstrap runtime이 transport server와 함께 시작/종료한다. -- CLI, ops console, HTTP/A2A 입력 표면, 후속 Control Plane wire handler가 같은 Edge operation을 노출할 때는 `service.Service` 같은 표면 중립 application service로 수렴시킨다. registry 변경, run dispatch, command request 로직을 표면별로 복제하지 않는다. -- `apps/edge/cmd/edge/**`, `apps/edge/internal/bootstrap/**`, `apps/edge/internal/transport/**`, `apps/edge/internal/service/**`, `apps/edge/internal/events/**`, `apps/edge/internal/input/**`, `apps/edge/internal/openai/**`, `apps/edge/internal/opsconsole/**`, `apps/edge/internal/node/**`, console/HTTP 입출력, run/node event relay를 바꾼 뒤에는 `testing` domain rule의 작업 후 검증 기준을 따른다. +- Control Plane connector는 disabled 설정 또는 빈 `wire_addr`에서 no-op이어야 한다. 연결 재시도와 stop은 Edge serve lifecycle에 묶는다. +- Control Plane status 응답은 `service.Service`의 `ListNodeSnapshots()` 같은 Edge-owned 표면에서 만든다. connector가 registry나 transport internals를 직접 읽어 제품 계약을 만들지 않는다. +- Control Plane node lifecycle event relay는 `events.Bus` 구독으로 수행하고, event metadata에는 필요한 Edge identity만 보강한다. +- CLI, ops console, HTTP/A2A 입력 표면, Control Plane wire connector가 같은 Edge operation을 노출할 때는 `service.Service` 같은 표면 중립 application service로 수렴시킨다. registry 변경, run dispatch, command request 로직을 표면별로 복제하지 않는다. +- `apps/edge/cmd/edge/**`, `apps/edge/internal/bootstrap/**`, `apps/edge/internal/controlplane/**`, `apps/edge/internal/transport/**`, `apps/edge/internal/service/**`, `apps/edge/internal/events/**`, `apps/edge/internal/input/**`, `apps/edge/internal/openai/**`, `apps/edge/internal/opsconsole/**`, `apps/edge/internal/node/**`, console/HTTP/Control Plane 입출력, run/node event relay를 바꾼 뒤에는 `testing` domain rule의 작업 후 검증 기준을 따른다. ## 다른 도메인과의 경계 - **node**: edge는 node 내부 adapter를 직접 실행하지 않는다. edge는 사전 등록 정보와 연결 registry를 기반으로 요청을 보낼 대상과 실행 설정을 관리하고, TCP/protobuf로 `RunRequest`/`CancelRequest`/`NodeCommandRequest`를 보낸다. - **platform-common**: edge 설정, metrics, protobuf 타입은 platform-common 계약을 따른다. - **external input surfaces**: OpenAI-compatible HTTP와 A2A JSON-RPC는 edge inbound adapter이며, 내부 transport/protobuf 경계를 대체하지 않는다. -- **control-plane**: control-plane은 Edge를 통해 시스템을 제어한다. 상시 운영, multi-edge 관찰, 정책/감사 UX는 control-plane으로 확장하되, Control Plane 없는 bootstrap/local/field/진단 fallback은 `iop-edge` command 표면에 남긴다. Node 직접 연결/직접 스케줄링을 control-plane 책임으로 굳히지 않는다. +- **control-plane**: control-plane은 Edge를 통해 시스템을 제어한다. Edge domain은 outbound connector와 Edge-owned status/event 응답을 소유하고, control-plane domain은 server endpoint와 Edge registry view를 소유한다. Control Plane 없는 bootstrap/local/field/진단 fallback은 `iop-edge` command 표면에 남긴다. ## 금지 사항 @@ -93,6 +102,7 @@ last_rule_updated_at: 2026-05-30 - OpenAI-compatible HTTP API를 추가할 때 내부 TCP/protobuf 경계를 우회하지 않는다. - A2A/OpenAI/console 입력 표면에서 registry client에 직접 `RunRequest`를 조립·전송하지 않는다. 공유 로직은 `service.Service`에 둔다. - Control Plane 구현 전이라도 Node를 Control Plane에 직접 연결하는 경로를 edge 도메인에 추가하지 않는다. +- Control Plane connector에서 Node token, Node address, transport client 내부 상태를 Control Plane status 계약으로 노출하지 않는다. - Control Plane 도입만을 이유로 `iop-edge config`, `env`, `node register`, `nodes list`, `smoke`, `setup` 같은 local/field fallback command 경로를 제거하거나 제품 기본 계약에서 제외하지 않는다. 축소는 별도 roadmap 결정과 대체 fallback 기준이 있을 때만 다룬다. - `node register`와 bootstrap UX에 named environment parameter 조합을 기본 사용자 경로로 노출하지 않는다. - gRPC 또는 WebSocket을 기본 내부 transport로 바꾸지 않는다. diff --git a/agent-ops/rules/project/domain/node/rules.md b/agent-ops/rules/project/domain/node/rules.md index cc5cd5e..a67053e 100644 --- a/agent-ops/rules/project/domain/node/rules.md +++ b/agent-ops/rules/project/domain/node/rules.md @@ -1,7 +1,7 @@ --- domain: node -last_rule_review_commit: 6e1c201d0b82661465238821edb136444eacb9d0 -last_rule_updated_at: 2026-05-28 +last_rule_review_commit: aaf35e61a91d33b81b64b1054649f7e28ad421c0 +last_rule_updated_at: 2026-05-31 --- # node @@ -44,6 +44,7 @@ Edge에 연결되어 실제 adapter execution을 수행하는 IOP 노드 에이 - `adapters.LifecycleAdapter` — start/stop lifecycle이 필요한 어댑터의 optional 인터페이스 - `adapters.BuildFromPayload()` — edge에서 받은 `NodeConfigPayload`로 `Registry`를 초기화하는 factory - `adapters/cli.CLI` — one-shot, persistent TUI, persistent-lazy, codex-exec, antigravity-print, opencode-sse profile을 실행하는 CLI adapter +- `adapters/cli.clineJSONEmitter` — Cline JSON output을 `RuntimeEvent` delta/error로 변환하는 emitter - `adapters/cli.executeAntigravityPrint()` — Antigravity print mode conversation id를 IOP logical session별로 보관하고 resume_args로 후속 요청을 재개 - `adapters/cli.executeOpencodeSSE()` — opencode serve HTTP/SSE session을 실행하거나 `--attach`로 외부 server에 연결해 delta를 relay - `adapters/cli.executePersistent()` — terminal/persistent profile의 completion marker, idle timeout, output filter를 처리 @@ -65,6 +66,7 @@ Edge에 연결되어 실제 adapter execution을 수행하는 IOP 노드 에이 - `adapters.Registry`의 start/stop은 bootstrap lifecycle에서만 호출하고 개별 adapter에서 직접 호출하지 않는다. - cli adapter의 출력 format별 파싱 로직은 `lineEmitter` 구현체로 분리하고 `node.Node`에 분기문으로 박지 않는다. - CLI profile mode별 세부 실행(`persistent-lazy`, `codex-exec`, `antigravity-print`, `opencode-sse`)은 `adapters/cli` 내부에 두고, `runtime.Adapter` 계약 밖으로 새 transport를 노출하지 않는다. +- `cline-json`, `opencode-json`, `codex-json`, `claude-json` 같은 provider별 stdout parser는 `adapters/cli` emitter로 등록하고 runtime 공통 이벤트로만 외부에 노출한다. - CLI logical session은 `(target, session_id)`로 식별한다. Antigravity conversation id, Codex external id, opencode session/server 상태를 전역 target 단위로 공유하지 않는다. - `response_idle_timeout_ms`, `startup_idle_timeout_ms`, `completion_marker`, `resume_args`, `mode` 같은 CLI profile 설정은 edge config/proto payload를 통해 주입하고 node 코드에 target별 상수를 늘리지 않는다. - Ollama adapter는 내부 target을 model 이름으로 사용하고, `context_size`는 `options.num_ctx` default로만 주입한다. 요청 input에 명시된 `options.num_ctx`가 있으면 이를 덮어쓰지 않는다. diff --git a/agent-ops/rules/project/domain/platform-common/rules.md b/agent-ops/rules/project/domain/platform-common/rules.md index fc0fa4f..3a55eca 100644 --- a/agent-ops/rules/project/domain/platform-common/rules.md +++ b/agent-ops/rules/project/domain/platform-common/rules.md @@ -1,7 +1,7 @@ --- domain: platform-common -last_rule_review_commit: 6e1c201d0b82661465238821edb136444eacb9d0 -last_rule_updated_at: 2026-05-28 +last_rule_review_commit: aaf35e61a91d33b81b64b1054649f7e28ad421c0 +last_rule_updated_at: 2026-05-31 --- # platform-common @@ -30,11 +30,13 @@ last_rule_updated_at: 2026-05-28 - `apps/node/` — node 실행 파이프라인과 adapter 관리 - `apps/edge/` — 실행 그룹 컨트롤러와 node registry - `apps/control-plane/` — 중앙 제어면 앱 구현 영역 +- `apps/client/` — Flutter client app과 Dart protobuf 생성물 사용 영역 - `apps/worker/` — worker 앱 구현 예정 영역 ## 주요 구성 요소 - `config.NodeConfig` / `config.EdgeConfig` — node/edge 앱 설정 계약 +- `config.EdgeInfo` / `config.EdgeControlPlaneConf` — Edge identity와 Control Plane outbound connector 설정 계약 - `config.EdgeServerConf` / `config.EdgeBootstrapConf` — Edge listen/advertise host와 artifact bootstrap URL 설정 계약 - `config.EdgeOpenAIConf` / `config.EdgeA2AConf` / `config.EdgeConsoleConf` — edge 입력 표면과 console 기본 설정 계약 - `config.CLIProfileConf` / `config.CompletionMarkerConf` — CLI adapter profile, mode, resume args, completion marker 설정 계약 @@ -46,6 +48,9 @@ last_rule_updated_at: 2026-05-28 - `policy.Engine` — 정책 적용/검증 계약 - `jobs.Job` — 비동기 작업 상태 placeholder; 내부 실행 대상은 `target`으로 표현 - `proto/iop/*.proto` — 앱 간 메시지 원본 계약 +- `ClientHelloRequest` / `ClientHelloResponse` — Client-Control Plane hello baseline 계약 +- `EdgeHelloRequest` / `EdgeHelloResponse` — Edge가 Control Plane으로 연결할 때 쓰는 hello baseline 계약 +- `EdgeStatusRequest` / `EdgeStatusResponse` / `EdgeNodeSnapshot` — Control Plane이 Edge-owned node snapshot을 조회하는 wire 계약 ## 유지할 패턴 @@ -53,18 +58,20 @@ last_rule_updated_at: 2026-05-28 - 설정 struct 필드 변경 시 YAML tag, mapstructure tag, default, `configs/*.yaml` 예시를 함께 확인한다. - host setup 기본 템플릿을 바꿀 때는 `packages/hostsetup`의 `EdgeSpec`/`NodeSpec`, 기본 경로, systemd unit, 관련 CLI `setup` 옵션과 함께 확인한다. - protobuf 계약 변경은 `proto/iop/*.proto`에서 시작하고 `make proto`로 Go 생성물을 갱신한다. +- Client가 소비하는 proto 계약을 변경하면 `make proto-dart`로 `apps/client/lib/gen/proto/iop/*.dart` 생성물도 갱신한다. - 생성 파일(`proto/gen/iop/*.pb.go`)은 사람이 직접 편집하지 않는다. - 공통 패키지는 작고 명확한 계약을 유지하고 앱별 정책을 과도하게 끌어올리지 않는다. - 공통 event helper는 envelope 생성과 상수 정의까지만 담당하고, edge 내부 fanout/replay/store 정책은 edge 도메인에 둔다. - `RunRequest`, `ExecutionSpec`, `NodeCommandRequest`, `CLIProfileConfig`, job/history 계열 계약을 변경할 때 내부 실행 용어는 `target`을 우선하고, `model`은 외부 호환 경계인지 확인한다. - Control Plane hello 계열 proto는 Edge/Node scheduling 계약으로 확장하지 않는다. +- Control Plane-Edge status proto는 Edge-owned snapshot을 표현한다. Node address, token, direct scheduling 필드를 싣지 않는다. - `packages/config/**`, `packages/events/**`, `packages/hostsetup/**`, `configs/**`, `proto/iop/**`처럼 edge-node 실행 설정, setup, lifecycle event, 메시지 계약에 영향을 주는 작업을 한 뒤에는 `testing` domain rule의 작업 후 검증 기준을 따른다. ## 다른 도메인과의 경계 - **node**: node가 필요로 하는 설정/타입/계약을 제공하지만 실행 파이프라인의 소유자는 node이다. - **edge**: edge가 필요로 하는 설정/관측성/protobuf 계약을 제공하지만 실행 그룹 제어와 node registry 동작의 소유자는 edge이다. -- **control-plane/worker**: 앱별 구현에 필요한 공통 타입만 이 영역으로 승격하고 앱 내부 책임은 각 도메인에 둔다. +- **control-plane/client/worker**: 앱별 구현에 필요한 공통 타입만 이 영역으로 승격하고 앱 내부 책임은 각 도메인에 둔다. ## 금지 사항 @@ -73,4 +80,5 @@ last_rule_updated_at: 2026-05-28 - edge fanout bus, web UI state, control-plane session 관리처럼 특정 앱의 운영 상태를 공통 패키지로 끌어올리지 않는다. - 내부 실행 계약을 확장하면서 `model` 중심 명명을 되살리지 않는다. 외부 API 호환이 필요한 경우 경계와 변환 위치를 명시한다. - protobuf 생성물을 직접 수정하지 않는다. +- Client Dart protobuf 생성물을 proto 원본과 불일치하게 두지 않는다. - 설정 파일만 바꾸고 `packages/config`의 로딩/default와 불일치하게 두지 않는다. diff --git a/agent-ops/rules/project/domain/testing/rules.md b/agent-ops/rules/project/domain/testing/rules.md index 88f0903..8b62e16 100644 --- a/agent-ops/rules/project/domain/testing/rules.md +++ b/agent-ops/rules/project/domain/testing/rules.md @@ -1,7 +1,7 @@ --- domain: testing -last_rule_review_commit: 6e1c201d0b82661465238821edb136444eacb9d0 -last_rule_updated_at: 2026-05-28 +last_rule_review_commit: aaf35e61a91d33b81b64b1054649f7e28ad421c0 +last_rule_updated_at: 2026-05-31 --- # testing @@ -15,23 +15,32 @@ last_rule_updated_at: 2026-05-28 - `Makefile` — 공식 test target, proto generation, 보조 smoke target을 선언하는 위치이다. - `scripts/dev/edge.sh` — repo 내부 edge console/server 개발 진단 helper이다. - `scripts/dev/node.sh` — repo 내부 node 연결 개발 진단 helper이다. field 사용자 기본 경로로 안내하지 않는다. -- `iop-edge bootstrap pack` — Node host bootstrap용 artifact 폴더를 준비하는 공식 command이다. +- `scripts/dev/web.sh` — repo 내부 Flutter Web client 개발 진단 helper이다. field 배포 기본 경로로 안내하지 않는다. - `scripts/e2e-smoke.sh` — mock/real profile 기반 보조 edge-node smoke 검증이다. - `scripts/e2e-openai-ollama.sh` — OpenAI-compatible Ollama 입력 표면 보조 smoke 검증이다. +- `scripts/e2e-control-plane-edge-wire.sh` — Control Plane-Edge wire hello/disconnect 보조 smoke 검증이다. +- `docker-compose.yml` — local dev용 Control Plane, datastore, Flutter Web client stack 조립 표면이다. ## 제외 경로 - `apps/node/` — node 실행 구현의 소유자는 node 도메인이다. testing 도메인은 작업 후 검증 기준만 정의한다. - `apps/edge/` — edge 실행 구현의 소유자는 edge 도메인이다. testing 도메인은 작업 후 검증 기준만 정의한다. +- `apps/control-plane/` — Control Plane 구현의 소유자는 control-plane 도메인이다. testing 도메인은 작업 후 검증 기준만 정의한다. +- `apps/client/` — Flutter client 구현과 client test 파일의 소유자는 client 도메인이다. testing 도메인은 작업 후 검증 기준만 정의한다. - `packages/` 및 `proto/` — 공통 계약의 소유자는 platform-common 도메인이다. testing 도메인은 해당 변경 후 필요한 검증 기준만 정의한다. ## 주요 구성 요소 - 대상 패키지 테스트 — 변경한 패키지와 인접한 패키지의 빠른 회귀 검증이다. - `go test ./...` — 저장소 전체 Go 테스트 회귀 검증이다. +- `flutter test` / `make client-test` — Flutter client 단위/widget/wire 테스트 검증이다. +- `make client-build-web` — Flutter Web build와 `--dart-define` 설정 주입 검증이다. - edge-node 개발 진단 흐름 검증 — `scripts/dev/edge.sh`와 `scripts/dev/node.sh`를 각각 실행하고, edge console에서 메시지 2회와 command 명령을 직접 보내 결과가 edge 화면에 도착하는지 확인하는 저수준 검증이다. field 사용자 기본 UX는 Edge가 제시한 bootstrap 명령이다. +- client 개발 진단 흐름 검증 — `scripts/dev/web.sh`로 Flutter Web dev server를 띄우고 Control Plane HTTP/WS URL 주입과 `/client` wire 연결 상태를 확인하는 저수준 검증이다. - 보조 E2E smoke — 임시 설정과 mock adapter로 최소 생존을 빠르게 확인하는 보조 검증이다. 이 결과만으로 완료 처리하지 않는다. - OpenAI-compatible Ollama smoke — `scripts/e2e-openai-ollama.sh`로 OpenAI HTTP 입력 표면이 edge service와 node adapter 경로로 수렴하는지 확인하는 보조 검증이다. +- Control Plane-Edge wire smoke — `scripts/e2e-control-plane-edge-wire.sh`로 실제 Control Plane/Edge 프로세스의 Edge hello, 연결 성공, disconnect marker를 확인하는 보조 검증이다. +- compose local dev stack 검증 — `docker-compose.yml` 변경 시 Control Plane, datastore, Flutter Web service의 build/env/healthcheck wiring을 확인하는 검증이다. - full-cycle 실제 구동 — 비효율적이어도 관련 사용자 명령과 실행 cycle을 한 번씩 실제 entrypoint로 통과시키는 검증이다. - 실제 외부 CLI 검증 — `claude`, `antigravity`, `codex`, `opencode`처럼 외부 CLI 설치와 계정/환경이 필요한 기준 profile을 실제 호출하는 검증이다. - one-line bootstrap/install UX — Node, OTO, specialized agent, Control Plane enrollment처럼 사용자가 대상 host에서 복사해 실행하는 연결/설치 명령의 사용자 경험 기준이다. @@ -40,7 +49,7 @@ last_rule_updated_at: 2026-05-28 - 테스트는 테스트 파일 변경 여부가 아니라 작업 영향 범위로 결정한다. - 사용자 실행 파이프라인에 닿는 작업을 한 경우, 작업 완료 후 일반 Go 테스트와 변경 범위에 맞는 full-cycle 실제 구동을 검증한다. repo 내부 edge-node 진단에는 `scripts/dev/edge.sh` + `scripts/dev/node.sh`를 사용할 수 있지만, field 사용자 기본 UX는 bootstrap 명령으로 확인한다. -- 사용자 실행 파이프라인에는 `scripts/dev/**`, `Makefile`, `apps/*/cmd/**`, `apps/*/internal/bootstrap/**`, edge-node transport/service/registry/input surface, adapter 실행/stream/cancel/status 경로, `configs/**`, `packages/config/**`, `packages/hostsetup/**`, 관련 protobuf 계약 변경이 포함된다. +- 사용자 실행 파이프라인에는 `scripts/dev/**`, `scripts/e2e-*.sh`, `Makefile`, `docker-compose.yml`, `apps/*/cmd/**`, `apps/*/internal/bootstrap/**`, edge-node transport/service/registry/input surface, Control Plane wire endpoint, client wire surface, adapter 실행/stream/cancel/status 경로, `configs/**`, `packages/config/**`, `packages/hostsetup/**`, 관련 protobuf 계약 변경이 포함된다. - edge-node 개발 진단 흐름 검증은 `scripts/dev/edge.sh`와 `scripts/dev/node.sh`를 별도 프로세스로 직접 실행하고, edge console prompt에 명령을 한 줄씩 입력한 뒤 기대 출력이 도착한 것을 확인하고 다음 입력으로 넘어간다. - 메시지 검증 기준은 edge console에서 같은 session으로 메시지 2회를 보내고, 각 요청마다 `[edge] sent`, `[node-*-event] start`, 비어 있지 않은 `[node-*-message]`, `[node-*-event] complete`가 edge 화면에 표시되는 것이다. - 완료 이벤트만으로 정상 판정하지 않는다. node 로컬 출력에 생성된 `[node-message]` payload가 edge console의 `[node-*-message]` 출력에 모두 표시되어야 하며, complete event는 모든 message payload가 edge에 도착한 뒤의 마감 신호로 본다. @@ -52,7 +61,9 @@ last_rule_updated_at: 2026-05-28 - one-line bootstrap/install command의 Edge 주소, artifact 주소, target, platform, config path 같은 값은 작업자/Edge/Control Plane이 미리 굽거나 완성해서 제공한다. 사용자 기본 경로에서 `IOP_*=` 같은 named environment parameter나 여러 주소 조합을 직접 입력하게 하지 않는다. - field Node bootstrap의 사용자 명령은 `curl -fsSL <완성된-bootstrap-url> | bash -s ` 형태를 기준으로 한다. 다른 bootstrap/enrollment 작업에서도 같은 수준의 단일 token UX를 우선 적용하고, 예외가 필요하면 사용자에게 먼저 확인한다. - repo 내부 edge-node 진단의 상세 수행 절차와 기능별 체크리스트는 `agent-ops/skills/project/e2e-smoke/SKILL.md`를 따른다. -- `make test-e2e`, `scripts/e2e-smoke.sh`, `scripts/e2e-openai-ollama.sh`는 보조 smoke 명령이다. 실행할 수 있으면 보조 확인으로 기록하되, full-cycle 실제 구동이나 field bootstrap 검증을 대체하지 않는다. +- `make test-e2e`, `scripts/e2e-smoke.sh`, `scripts/e2e-openai-ollama.sh`, `scripts/e2e-control-plane-edge-wire.sh`는 보조 smoke 명령이다. 실행할 수 있으면 보조 확인으로 기록하되, full-cycle 실제 구동이나 field bootstrap 검증을 대체하지 않는다. +- Client-Control Plane wire나 client UI를 바꾸면 `make client-test`를 기본 검증으로 기록한다. Web build/deploy 경로를 바꾸면 `make client-build-web`, `scripts/dev/web.sh`, compose build 중 변경 범위에 맞는 경로를 추가 확인한다. +- Control Plane-Edge wire나 Edge outbound connector를 바꾸면 대상 Go 테스트와 함께 `make test-control-plane-edge-wire`를 보조 검증으로 기록한다. status snapshot, node event relay, HTTP `/edges` 조회를 바꾼 경우 해당 동작을 별도로 확인한다. - `iop-edge bootstrap pack`, `make pack-edge`, 내장 artifact server 변경 시 최소 현재 host target build를 실행하고 archive 압축 해제, artifact 폴더 위치, checksum 생성, node bootstrap script가 positional token UX를 유지하는지 확인한다. - 풀테스트에서는 실제 외부 CLI profile 검증을 필수로 수행한다. 환경, 계정, provider, 원격 endpoint 문제로 호출할 수 없거나 실패한 profile은 누락하지 말고 profile별 실패 또는 blocker로 보고한다. - 작업 최종 보고에는 실행한 테스트 명령, repo 내부 edge-node 진단 수행 여부, 보조 E2E smoke 수행 여부, full-cycle 실제 구동 수행 여부를 명시한다. 수행하지 못한 필수 검증은 이유와 남은 위험을 함께 적는다. @@ -106,12 +117,14 @@ terminated session default node=test-node - **node**: node는 adapter 실행과 edge 연결 구현을 소유한다. testing은 node 변경 후 어떤 검증을 거칠지 정한다. - **edge**: edge는 registry, service, transport, console, HTTP/A2A input surface 구현을 소유한다. testing은 edge 변경 후 사용자 실행 흐름을 어떻게 확인할지 정한다. +- **control-plane**: control-plane은 server endpoint, Edge wire registry, Client WS boundary 구현을 소유한다. testing은 해당 변경 후 어떤 smoke와 실제 구동 검증을 거칠지 정한다. +- **client**: client는 Flutter UI와 Dart wire wrapper 구현을 소유한다. testing은 client 변경 후 어떤 Flutter test/build/dev-server 검증을 거칠지 정한다. - **platform-common**: platform-common은 config/proto 계약을 소유한다. testing은 해당 계약 변경이 edge-node 실행 흐름에 닿을 때 필요한 검증을 정한다. ## 금지 사항 - 사용자 실행 파이프라인에 닿는 변경을 하고 유닛/패키지 테스트만으로 완료 처리하지 않는다. -- `make test-e2e`, `scripts/e2e-smoke.sh`, `scripts/e2e-openai-ollama.sh`, 또는 smoke 통과 출력만으로 완료 처리하지 않는다. +- `make test-e2e`, `scripts/e2e-smoke.sh`, `scripts/e2e-openai-ollama.sh`, `scripts/e2e-control-plane-edge-wire.sh`, 또는 smoke 통과 출력만으로 완료 처리하지 않는다. - 관련 작업 후 full-cycle 실제 구동을 비용이 크다는 이유만으로 생략하지 않는다. - 보조 E2E smoke를 외부 CLI 설치, 로그인, 네트워크 계정 상태에 의존하게 만들지 않는다. - 검증을 위해 기본 `configs/*.yaml`을 임시값으로 오염시키지 않는다. 임시 설정 파일이나 환경 변수 override를 사용한다. diff --git a/agent-ops/rules/project/rules.md b/agent-ops/rules/project/rules.md index 8ae7f65..9756721 100644 --- a/agent-ops/rules/project/rules.md +++ b/agent-ops/rules/project/rules.md @@ -7,17 +7,18 @@ ## 프로젝트 개요 -- IOP(Inference Operations Platform)는 Control Plane - Edge - Node 계층 구조를 기반으로 모델 서빙과 CLI Agent/Automation 실행을 함께 다루는 실행 오케스트레이션 모노레포이다. 핵심 서비스는 Go이다. +- IOP(Inference Operations Platform)는 Control Plane - Edge - Node 계층 구조를 기반으로 모델 서빙과 CLI Agent/Automation 실행을 함께 다루는 실행 오케스트레이션 모노레포이다. 핵심 서비스는 Go이고 운영 client는 Flutter/Dart이다. - 내부 실행 개념은 model 중심이 아니라 `adapter + target` 중심으로 정리한다. 외부 OpenAI-compatible 경계나 외부 CLI 인자에서는 호환성을 위해 `model` 표현이 남을 수 있다. -- 현재 구현 중심은 `apps/node`와 `apps/edge`의 Edge-Node 실행 경로, `iop-edge` command 중심의 local/field 운영 UX, OpenAI-compatible/A2A 입력 표면, CLI adapter logical session/runtime이다. -- `apps/control-plane`은 health/readiness HTTP와 proto-socket WebSocket hello baseline을 가진 중앙 제어면 scaffold이다. +- 현재 구현 중심은 `apps/node`와 `apps/edge`의 Edge-Node 실행 경로, `apps/control-plane`의 Control Plane-Edge/Client wire baseline, `apps/client`의 Flutter 운영 UI, `iop-edge` command 중심의 local/field 운영 UX, OpenAI-compatible/A2A 입력 표면, CLI adapter logical session/runtime이다. +- `apps/control-plane`은 health/readiness HTTP, Client proto-socket WebSocket, Edge proto-socket TCP 연결 baseline을 가진 중앙 제어면이다. - `apps/worker`는 CLI placeholder 수준이므로 본격 구현 전 별도 domain rule을 만들거나 갱신한다. ## 주요 구조 - `apps/node/` — Edge에 연결되는 실행자. 런타임 라우팅, adapter execution, CLI/model runtime 실행, 현재 단계의 로컬 실행 이력 저장을 담당한다. - `apps/edge/` — 여러 Node를 묶는 백엔드 실행 그룹 컨트롤러. token 기반 등록, node registry, node 설정 전달, routing, stream relay, ops console, OpenAI-compatible/A2A 입력 표면을 담당한다. -- `apps/control-plane/` — 여러 Edge를 연결하고 상태 조회/설정 변경/명령 전달/이벤트 수신/운영 제어 API 제공을 담당할 Go 기반 중앙 관리 서버 scaffold이다. +- `apps/control-plane/` — 여러 Edge를 연결하고 상태 조회/설정 변경/명령 전달/이벤트 수신/운영 제어 API 제공을 담당할 Go 기반 중앙 관리 서버이다. +- `apps/client/` — Control Plane을 통해 Edge/Node 운영 상태를 보여주는 Flutter client이다. - `apps/worker/` — 비동기 작업 처리 예정 영역이다. 현재 placeholder이다. - `packages/` — 설정, 인증, 이벤트 helper, host setup, 정책, 메타데이터, 작업, 관측성, 버전 등 공통 패키지이다. - `proto/iop/` — IOP 메시지 계약 원본이다. @@ -42,6 +43,7 @@ - 저장소: `modernc.org/sqlite` - 메시지 계약: `google.golang.org/protobuf`, `proto/iop/*.proto` - 내부 소켓: `git.toki-labs.com/toki/proto-socket/go` +- Client: Flutter/Dart, `proto_socket` Dart client, Dart protobuf 생성물 ## 프로젝트 특화 컨벤션 @@ -75,12 +77,14 @@ | `apps/node/**` | node | `agent-ops/rules/project/domain/node/rules.md` | | `apps/edge/**` | edge | `agent-ops/rules/project/domain/edge/rules.md` | | `apps/control-plane/**` | control-plane | `agent-ops/rules/project/domain/control-plane/rules.md` | +| `apps/client/**` | client | `agent-ops/rules/project/domain/client/rules.md` | | `packages/**` | platform-common | `agent-ops/rules/project/domain/platform-common/rules.md` | | `proto/**` | platform-common | `agent-ops/rules/project/domain/platform-common/rules.md` | | `configs/**` | platform-common | `agent-ops/rules/project/domain/platform-common/rules.md` | | `scripts/dev/**` | testing | `agent-ops/rules/project/domain/testing/rules.md` | | `scripts/e2e-*.sh` | testing | `agent-ops/rules/project/domain/testing/rules.md` | | `Makefile` | testing | `agent-ops/rules/project/domain/testing/rules.md` | +| `docker-compose.yml` | testing | `agent-ops/rules/project/domain/testing/rules.md` | ## 도메인 후보