194 lines
13 KiB
Markdown
194 lines
13 KiB
Markdown
# 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 추가, 화면 정의 갱신, 화면 정의서 갱신 | `agent-ops/skills/common/update-agent-ui/SKILL.md` 수행 |
|
||
| agent-ui 검증, agent-ui validate, UI 정의 정합성 확인, wireframe 정합성 확인, UI 스캐폴드 검사 | `agent-ops/skills/common/validate-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` 수행 |
|