# oto 프로젝트 규칙

## 응답 언어

한국어로 응답한다.

## 프로젝트 개요

OTO는 YAML 파일로 정의된 빌드/배포 파이프라인을 실행하는 Dart runner에서 출발해, Flutter client와 Go core service를 함께 가진 모노레포형 CI/CD runner/control plane 제품이다.
Jenkins, FTP, Git, Slack/Mattermost, iOS/Flutter/Android 빌드 등 다양한 CI/CD 작업을 runner 커맨드로 추상화하고, 필요하면 OTO Server가 runner registry, bootstrap, job, log, artifact 상태를 소유한다.

## 실행 흐름

### 로컬 runner 실행

```text
apps/runner/bin/main.dart
  -> CLI / CommandManager
    -> Application
      -> DataComposer  # YAML + Jenkins env 파싱
        -> Pipeline
          -> Command.byType(CommandType) -> Command.execute(DataCommand)
```

### Control Plane 실행

```text
services/core/cmd/oto-core/main.go
  -> httpserver
    -> runnerregistry / cicdstate

apps/client/lib/main.dart
  -> oto_client_app
    -> oto_console package
```

## 주요 구조

```text
apps/
├── runner/                      # Dart CLI runner/runtime
│   ├── bin/main.dart            # runner CLI entrypoint
│   ├── lib/cli/                 # CLI 레이어
│   ├── lib/oto/agent/           # OTO Server runner 등록/agent 실행
│   ├── lib/oto/application.dart # runner 오케스트레이터, BuildType enum
│   ├── lib/oto/commands/        # typed command 구현체
│   ├── lib/oto/core/            # 태그 시스템, 데이터 합성, 실행 컨텍스트
│   ├── lib/oto/data/            # JSON 직렬화 데이터 모델
│   ├── lib/oto/pipeline/        # 파이프라인 실행 엔진
│   └── assets/                  # sample YAML, script, packaging asset
├── client/                      # Flutter OTO client app
│   └── lib/src/app/             # app bootstrap/shell
services/
└── core/                        # Go OTO Server
    ├── cmd/oto-core/            # server entrypoint
    ├── internal/httpserver/     # HTTP API
    ├── internal/cicdstate/      # job/execution/log/artifact state
    └── internal/runnerregistry/ # runner registration state
packages/
└── flutter/oto_console/         # Flutter embeddable OTO console package
proto/                           # OTO runner/server protobuf contract
```

> 참고: root `lib/` 기반 구조와 `lib/framework/` 모듈은 현재 구조가 아니다. runner 코드는 `apps/runner/lib/` 아래에 있고, `dart_framework`는 `apps/runner/pubspec.yaml`의 외부 Git 의존성으로만 사용한다.

## 기술 스택

- runner: Dart SDK `>=3.8.0 <4.0.0`, `apps/runner/pubspec.yaml`
- client: Flutter/Dart SDK `^3.11.3`, `apps/client/pubspec.yaml`
- console package: Flutter/Dart SDK `^3.11.3`, `packages/flutter/oto_console/pubspec.yaml`
- core service: Go `1.26`, `services/core/go.mod`
- runner 주요 의존성: `dart_framework` Git dependency, `proto_socket` 상위 workspace path dependency, `http`, `json_annotation`, `yaml`, `cron`, `xml`, `protobuf`, `fixnum`, `resource_importer`
- client 주요 의존성: Flutter SDK, `agent_shell` 상위 workspace path dependency, `oto_console` local path dependency
- protobuf 생성: `make proto-go`, `make proto-dart`
- Dart 코드 생성: json_serializable + build_runner (`cd apps/runner && dart run build_runner build`)

## BuildType

| Value | 설명 |
|-------|------|
| `jenkins` | CI 모드 – Jenkins 환경 변수에서 워크스페이스·환경 읽기 |
| `test` | 로컬 테스트 모드 – 하드코딩된 테스트 데이터 사용 |
| `file` | 로컬 파일 경로에서 파이프라인 YAML 읽기 |
| `scheduler` | 스케줄러 데몬 모드 |

## 태그 시스템 (`apps/runner/lib/oto/core/tag_system.dart`)

- **읽기 태그** `<!namespace.key>` — 런타임 저장소에서 값 치환
- **쓰기 태그** `<@namespace.key>` — 커맨드 결과를 property에 저장

태그가 문자열 전체인 경우 원본 타입 유지 (List 등), 부분 삽입이면 toString() 변환.

## 데이터 모델 컨벤션

- 모든 커맨드 파라미터는 `DataParam` (`apps/runner/lib/oto/data/base_data.dart`) 상속
- JSON 직렬화: `json_annotation` 사용, 생성 파일은 `*.g.dart`
- `CommandSpec` — 커맨드의 category, dataModel, samplePath 메타데이터
- `DataCommon` – Jenkins 환경 데이터 (workspace, job name, build number 등)
- `DataCommand` (`command_data.dart`) – 모든 `Command.execute()`에 전달되는 통합 컨테이너

## 새 커맨드 추가 절차

1. `apps/runner/lib/oto/data/*_data.dart`에 데이터 모델 정의 (`DataParam` 상속)
2. `apps/runner/lib/oto/commands/command.dart`의 `CommandType` enum에 값 추가
3. `Command` 상속하여 커맨드 클래스 구현
4. `Command.register(..., spec: CommandSpec(...))`에 구현체/데이터 모델/샘플 경로 등록
5. `apps/runner/lib/oto/commands/command_registry.dart`의 `registerAllCommands()`에 등록 함수 연결
6. 관련 `apps/runner/assets/yaml/sample/**` 샘플 갱신 필요 여부 확인
7. `@JsonSerializable` 클래스 추가/변경 시 `cd apps/runner && dart run build_runner build` 실행

## 에러 처리

- `Application.build()`는 `catch (e, stacktrace)` (Exception이 아닌 Error 포함 캐치)
- 에러 시 `BuildResult.failure(..., exitCode: 10)`을 반환하고 CLI 진입점이 부모 프로세스에 실패 exit code를 전달

## 스킬 기반 작업 흐름

- agent-ops 초기화, domain rule 생성, skill 생성, commit/push, agent-ops sync 계열 요청은 사용자가 명시적으로 요청한 경우에만 `agent-ops/skills/common/router.md`를 먼저 읽고 해당 `SKILL.md`를 따른다.
- agent-ui 생성/갱신/검증/코드 동기화, UI 스캐폴드, 화면 정의서, view/component/frame/wireframe 정의 요청은 `agent-ops/skills/common/router.md`를 먼저 읽고 해당 `SKILL.md`를 따른다.
- 도메인 룰 갱신/검토 요청은 `agent-ops/skills/common/update-domain-rule/SKILL.md`를 따른다.
- 새 도메인 rule 생성이 필요한 경우 `agent-ops/skills/common/create-domain-rule/SKILL.md`를 따른다.
- YAML 작성 요청은 코드 분석보다 `sample` 도메인 rule과 `apps/runner/assets/yaml/sample/**`를 우선 참조한다.
- 코드 변경 요청은 먼저 아래 도메인 매핑에서 해당 rule을 읽고, 변경 후 도메인 rule의 검증 기준을 따른다.

## 도메인 룰 로딩

- 아래 도메인 매핑에 해당하는 작업에서 해당 domain 최초 진입 시 domain rule을 1회 읽는다.
- 더 구체적인 경로 패턴이 있으면 그 rule을 우선 적용한다.
- 이미 읽은 domain rule은 같은 세션에서 반복해서 읽지 않는다.

## 도메인 매핑

| 경로 패턴 | 도메인 | rules.md |
|----------|--------|----------|
| `apps/runner/bin/main.dart` | cli | `agent-ops/rules/project/domain/cli/rules.md` |
| `apps/runner/lib/resources.resource_importer.dart` | cli | `agent-ops/rules/project/domain/cli/rules.md` |
| `apps/runner/lib/oto/pipeline/**` | pipeline | `agent-ops/rules/project/domain/pipeline/rules.md` |
| `apps/runner/lib/oto/commands/**` | command | `agent-ops/rules/project/domain/command/rules.md` |
| `apps/runner/lib/oto/data/pipeline_data.dart` | pipeline | `agent-ops/rules/project/domain/pipeline/rules.md` |
| `apps/runner/lib/oto/data/**` | command | `agent-ops/rules/project/domain/command/rules.md` |
| `apps/runner/lib/cli/commands/command_agent.dart` | agent | `agent-ops/rules/project/domain/agent/rules.md` |
| `apps/runner/lib/cli/commands/scheduler/**` | scheduler | `agent-ops/rules/project/domain/scheduler/rules.md` |
| `apps/runner/lib/cli/commands/command_scheduler.dart` | scheduler | `agent-ops/rules/project/domain/scheduler/rules.md` |
| `apps/runner/lib/cli/**` | cli | `agent-ops/rules/project/domain/cli/rules.md` |
| `apps/runner/lib/oto/agent/**` | agent | `agent-ops/rules/project/domain/agent/rules.md` |
| `apps/runner/lib/oto/application.dart` | core | `agent-ops/rules/project/domain/core/rules.md` |
| `apps/runner/lib/oto/core/**` | core | `agent-ops/rules/project/domain/core/rules.md` |
| `apps/runner/lib/oto/utils/**` | core | `agent-ops/rules/project/domain/core/rules.md` |
| `apps/runner/assets/script/**/jenkins_env_params.*` | core | `agent-ops/rules/project/domain/core/rules.md` |
| `apps/runner/assets/template/**` | command | `agent-ops/rules/project/domain/command/rules.md` |
| `apps/runner/assets/yaml/sample/**` | sample | `agent-ops/rules/project/domain/sample/rules.md` |
| `apps/runner/assets/package/**` | cli | `agent-ops/rules/project/domain/cli/rules.md` |
| `apps/runner/assets/bin/**` | cli | `agent-ops/rules/project/domain/cli/rules.md` |
| `apps/runner/assets/script/shell/oto_agent_bootstrap.sh` | agent | `agent-ops/rules/project/domain/agent/rules.md` |
| `apps/runner/assets/script/powershell/oto_agent_bootstrap.ps1` | agent | `agent-ops/rules/project/domain/agent/rules.md` |
| `apps/runner/assets/script/**` | cli | `agent-ops/rules/project/domain/cli/rules.md` |
| `Makefile` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `apps/runner/pubspec.yaml` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `apps/runner/pubspec.lock` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `apps/runner/analysis_options.yaml` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `apps/client/pubspec.yaml` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `apps/client/pubspec.lock` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `apps/client/analysis_options.yaml` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `packages/flutter/oto_console/pubspec.yaml` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `packages/flutter/oto_console/pubspec.lock` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `packages/flutter/oto_console/analysis_options.yaml` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `services/core/go.mod` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `services/core/go.sum` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `services/core/oto/**` | framework | `agent-ops/rules/project/domain/framework/rules.md` |
| `proto/**` | framework | `agent-ops/rules/project/domain/framework/rules.md` |

`test/**`는 별도 도메인으로 고정하지 않는다. 테스트 변경 시 검증 대상 production 경로의 domain rule을 읽고, 여러 도메인을 검증하는 테스트면 관련 domain rule을 함께 읽는다.

`apps/client/lib/**`, `packages/flutter/oto_console/lib/**`, `services/core/internal/**`처럼 아직 dedicated domain rule이 없는 구현 경로는 기존 domain rule로 단정하지 않는다. 해당 경로의 동작 변경 전에는 관련 roadmap Milestone 또는 domain rule 갱신/생성을 먼저 확인한다.

## 스킬 라우팅

| 요청 키워드 | 수행 방법 |
|------------|---------|
| agent-ops 세팅해줘, scaffold 만들어줘, 초기화해줘 | `agent-ops/skills/common/init-agent-ops/SKILL.md` 수행 |
| yaml 짜줘, 파이프라인 만들어줘, 자동화 작성, 빌드 yaml | `agent-ops/rules/project/domain/sample/rules.md` 읽고 해당 샘플 참조 |
| 도메인 업데이트, domain rule 갱신, 도메인 검토, domain 스캔 | `agent-ops/skills/common/update-domain-rule/SKILL.md` 수행 |
| domain rule 만들어줘, rules.md 생성, 새 도메인 규칙 | `agent-ops/skills/common/create-domain-rule/SKILL.md` 수행 |
| skill 만들어줘, SKILL.md 생성, 새 스킬 추가 | `agent-ops/skills/common/create-skill/SKILL.md` 수행 |
| agent-ui 생성, UI 스캐폴드 생성, UI 정의 구조 생성, 화면 정의 구조 생성, agent-ui scaffold | `agent-ops/skills/common/create-agent-ui/SKILL.md` 수행 |
| agent-ui 갱신, agent-ui 업데이트, view 추가, component 추가, frame 추가, wireframe 추가, 화면 정의 갱신, 화면 정의서 갱신, 화면정의서 수정, 화면 정의서에 추가, 화면 정의서에서 제거, 와이어프레임 수정, 와이어프레임에 추가, wireframe 수정, wireframe에 추가, agent-ui USER_REVIEW 해결, UIR 결정 반영 | `agent-ops/skills/common/update-agent-ui/SKILL.md` 수행 |
| agent-ui 검증, agent-ui validate, UI 정의 정합성 확인, wireframe 정합성 확인, 화면정의서와 wireframe 맞춰, 화면 정의서와 와이어프레임 정합성 확인, 화면정의서와 코드가 안 맞아, UI 스캐폴드 검사 | `agent-ops/skills/common/validate-agent-ui/SKILL.md` 수행 |
| agent-ui와 코드 동기화, agent-ui와 코드 전체 동기화, 화면정의서대로 코드 반영, 화면 정의서 변경사항 구현, wireframe 변경사항 구현 | `agent-ops/skills/common/sync-agent-ui/SKILL.md` 수행 |
| 로드맵 만들어줘, roadmap 생성, 마일스톤 설계, goal/phase 구조 잡아줘 | `agent-ops/skills/common/create-roadmap/SKILL.md` 수행 |
| 로드맵 업데이트, roadmap 갱신, 마일스톤 갱신, phase 변경, 현재 마일스톤 변경, 로드맵 한국어 전환, 로드맵 번역 | `agent-ops/skills/common/update-roadmap/SKILL.md` 수행 |
| 계획 세워줘, 구현 계획, PLAN.md, plan | `agent-ops/skills/common/plan/SKILL.md` 수행 |
| 코드 리뷰해줘, 리뷰 진행해, 리뷰해줘, code review, CODE_REVIEW.md, 리뷰 루프 | `agent-ops/skills/common/code-review/SKILL.md` 수행 |
| 커밋해줘, 푸시해줘, commit, push, 반영해줘 | `agent-ops/skills/common/commit-push/SKILL.md` 수행 |
| agent-ops 싱크해, agent-ops 동기화해, agentic-framework에 올려줘, agent-ops를 [프로젝트]로 싱크해 | `agent-ops/skills/common/sync-push/SKILL.md` 수행 |
| agent-ops pull해, agent-ops 가져와, agentic-framework에서 가져와, agent-ops 내려받아 | `agent-ops/skills/common/sync-pull/SKILL.md` 수행 |
