diff --git a/.aiexclude b/.aiexclude new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.aiexclude @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..0c2baa3 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "deny": [ + "Read(./agent-task/archive/**)" + ] + } +} diff --git a/.clineignore b/.clineignore new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.clineignore @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.clinerules b/.clinerules new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/.clinerules @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/.geminiignore b/.geminiignore new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.geminiignore @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.gitignore b/.gitignore index 01c1827..99015c7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,18 @@ build/ .gito/ tmp/ + +# BEGIN Agent-Ops managed gitignore +!agent-task/ +!agent-task/**/ +!agent-task/**/*.md +!agent-task/**/*.log +agent-roadmap/current.md +# END Agent-Ops managed gitignore + +# Agent-Ops Private Rules +agent-ops/rules/private/ + +# Agent-Test Local Environment +agent-test/local/ +agent-test/runs/ diff --git a/AGENTS.md b/AGENTS.md index b25954b..d0488a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,34 +1,44 @@ -# Gito Agent Instructions +# 공통 규칙 -Read this file before making changes in this repository. +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** -## Common Rules +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. -- Prefer the existing structure over creating new top-level patterns. -- Keep user-facing final answers in Korean unless the user asks otherwise. -- Check the relevant domain rule before code changes. -- Do not expand scope beyond the current request. -- Do not write secrets, tokens, passwords, private endpoints, or raw credentials to tracked files. -- Use `rg` or `rg --files` for searches when available. -- Use root helpers under `bin/` before ad hoc commands when they cover the task. - -## Required First Reads - -Read these files once per session when they exist: +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. 1. `agent-ops/rules/project/rules.md` 2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` -## Domain Rules +# 프로젝트 간 잠금 -| Path | Domain | Rules | -| --- | --- | --- | -| `services/core/**` | core | `agent-ops/rules/project/domain/core/rules.md` | -| `apps/client/**` | client | `agent-ops/rules/project/domain/client/rules.md` | -| `packages/contracts/**` | contracts | `agent-ops/rules/project/domain/contracts/rules.md` | -| `bin/**`, `docs/**`, `AGENTS.md`, `README.md`, `agent-ops/**`, `agent-test/**` | workspace-ops | `agent-ops/rules/project/domain/workspace-ops/rules.md` | +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. -## Test Rules +# 스킬 규칙 -When validation is part of the task, read `agent-test/local/rules.md` first. +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/README.md b/README.md index 8bebcf0..4cd2f81 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,100 @@ # Gito -Gito is a platformless Git control plane for agent-driven development workflows. +Gito는 agent-driven development를 위한 platformless Git control plane이다. +여러 Git repository를 등록하고, workspace lease, Git operation, agent-shell, +IOP 실행, provider adapter, normalized event를 하나의 제어 흐름으로 묶는다. -It manages registered repositories, workspace leases, Git operations, agent-shell -execution, provider adapters, and normalized events. Git itself stays -platform-neutral; GitHub, GitLab, Gitea, Plane, Jira, and similar systems are -integrated through adapters. +Git 자체 작업은 GitHub/GitLab/Gitea 같은 플랫폼을 몰라도 동작해야 한다. +PR/MR, review, checks, webhook 같은 플랫폼 기능은 provider adapter 뒤에 둔다. -## Direction +## 현재 상태 -- Server: Go -- Control surface: Flutter app and `gitoctl` in future slices -- State: PostgreSQL as source of truth -- Event acceleration: Redis optional, not required for the first slice -- Internal transport: proto-socket first -- REST: health/readiness, smoke/curl, provider callbacks, simple admin bootstrap -- gRPC: intentionally excluded from the initial design +초기 scaffold 단계다. Go core는 build/test 가능한 최소 골격을 갖고 있고, +Flutter client는 control surface 경계만 잡혀 있다. -## Runtime Shape +현재 가능한 범위: + +- Go `server`, `worker`, `shell` command build +- health/readiness REST placeholder +- platformless `gitengine` 최소 테스트 +- core domain DTO 후보 +- provider-neutral `ChangeRequest` adapter 경계 +- Postgres migration 초기안 +- `agent-ops` 규칙/스킬 기반 작업 진입점 + +아직 구현 전인 범위: + +- 실제 proto-socket channel/action +- 실제 PostgreSQL store wiring +- agent-shell outbound 연결과 command streaming +- IOP CLI 실행 orchestration +- GitHub/GitLab/Gitea provider adapter +- Flutter 화면 구성과 operation event 구독 + +## 빠른 시작 + +```sh +bin/test +bin/lint +bin/build +``` + +빌드가 끝나면 root `bin/` 아래에 아래 실행 파일이 생성된다. + +```text +bin/gito-server +bin/gito-worker +bin/gito-shell +``` + +서버 scaffold 실행: + +```sh +HTTP_ADDR=:8080 bin/gito-server +``` + +확인: + +```sh +curl http://127.0.0.1:8080/healthz +curl http://127.0.0.1:8080/readyz +``` + +## 주요 명령 + +| 목적 | 명령 | 비고 | +| --- | --- | --- | +| 전체 테스트 | `bin/test` | Go test 실행, Flutter test scaffold가 없으면 client test는 skip | +| lint | `bin/lint` | `go vet ./...`, Flutter가 있으면 `flutter analyze --no-fatal-infos` | +| build | `bin/build` | `gito-server`, `gito-worker`, `gito-shell` 생성 | +| Core test | `cd services/core && go test ./...` | Go module 기준 직접 실행 | +| Core lint | `cd services/core && go vet ./...` | Go vet 직접 실행 | + +## 구조 + +| 경로 | 역할 | +| --- | --- | +| `services/core/cmd/server/` | control plane server entrypoint | +| `services/core/cmd/worker/` | operation worker entrypoint | +| `services/core/cmd/shell/` | agent-shell runner entrypoint | +| `services/core/internal/controlplane/` | REST/proto-socket surface, auth/policy gate 후보 | +| `services/core/internal/core/` | provider-neutral domain model | +| `services/core/internal/gitengine/` | platformless Git operation layer | +| `services/core/internal/worker/` | operation 실행 orchestration 후보 | +| `services/core/internal/agentshell/` | local command/IOP execution boundary | +| `services/core/internal/provider/` | GitHub/GitLab/Gitea/Plane/Jira adapter boundary | +| `services/core/internal/events/` | normalized event model | +| `services/core/internal/storage/` | PostgreSQL store boundary | +| `services/core/migrations/` | database schema migration | +| `apps/client/` | Flutter control surface scaffold | +| `packages/contracts/` | proto-socket, REST, event, DTO contract notes | +| `docs/` | 사람용 architecture/operation 문서 | +| `agent-ops/` | agent rules, skills, roadmap helper | +| `agent-test/` | local validation rules | + +## 설계 방향 + +Gito는 처음에는 modular monolith로 시작하되, 실행 역할은 분리 가능하게 둔다. ```text Flutter UI / gitoctl @@ -33,27 +110,62 @@ Agent Shell / IOP / Git Engine Provider Adapters ``` -## Repository Layout +원칙: -```text -apps/client/ Flutter control surface scaffold -services/core/ Go control plane, worker, shell commands -packages/contracts/ proto-socket, REST, event, and provider contract notes -docs/ Architecture and operation notes -bin/ Root test/lint/build helpers -agent-ops/ Agent operating rules -agent-test/ Local validation rules -``` +- `controlplane`은 요청, 정책, 관찰, event stream을 소유한다. +- `core`는 repo, workspace lease, operation, revision cursor, event 원장을 소유한다. +- `worker`는 operation 실행을 조율한다. +- `agentshell`은 실제 workspace command, Git CLI, IOP CLI 실행을 맡는다. +- `gitengine`은 platformless여야 하며 local bare repo만으로 테스트 가능해야 한다. +- `provider`는 PR/MR/review/checks/webhook 같은 플랫폼 기능만 맡는다. +- PostgreSQL은 source of truth이고, Redis는 필요할 때 fanout/stream 가속 계층으로만 둔다. -## First Commands +## 통신 정책 -```sh -bin/test -bin/lint -bin/build -``` +- 내부 runtime 통신은 proto-socket을 우선한다. +- REST는 health/readiness, provider callback, smoke/curl, 단순 bootstrap에만 둔다. +- gRPC는 초기 설계에서 제외한다. +- webhook은 빠른 wakeup signal로만 보고, 최종 판정은 Git revision/diff 또는 provider read API로 재검증한다. -The initial Go scaffold is intentionally small but buildable. Flutter platform -directories are not generated yet; `apps/client` currently records the control -surface boundary and a minimal Dart entry point. +## 작업 맥락 +작업을 시작할 때는 먼저 [AGENTS.md](AGENTS.md)를 읽고, 변경 경로에 맞는 domain rule을 확인한다. + +주요 문서: + +- [Project rules](agent-ops/rules/project/rules.md) +- [Core domain rules](agent-ops/rules/project/domain/core/rules.md) +- [Client domain rules](agent-ops/rules/project/domain/client/rules.md) +- [Contracts domain rules](agent-ops/rules/project/domain/contracts/rules.md) +- [Workspace ops rules](agent-ops/rules/project/domain/workspace-ops/rules.md) +- [Local test rules](agent-test/local/rules.md) +- [Architecture](docs/architecture.md) +- [Control plane contract candidates](packages/contracts/notes/gito-control-plane.md) + +## 개발 흐름 + +1. 변경 전에 관련 domain rule을 읽는다. +2. core 변경은 `cd services/core && go test ./...` 또는 `bin/test`로 확인한다. +3. contracts 변경은 README/notes의 transport 정책과 맞는지 확인한다. +4. client 변경은 Flutter toolchain이 있으면 `flutter analyze --no-fatal-infos`와 `flutter test`를 실행한다. +5. 생성된 binary, `.dart_tool/`, local secret 파일은 commit하지 않는다. + +## 환경 변수 + +| 이름 | 설명 | 필수 | +| --- | --- | --- | +| `APP_ENV` | runtime 환경 이름, 기본값 `local` | 아니오 | +| `HTTP_ADDR` | server listen address, 기본값 `:8080` | 아니오 | +| `DATABASE_URL` | PostgreSQL connection string | 실제 storage wiring 이후 필요 | +| `REDIS_URL` | optional Redis endpoint | 아니오 | +| `PROTO_SOCKET_PATH` | proto-socket endpoint path, 기본값 `/proto-socket` | 아니오 | +| `WORKER_ENABLED` | worker 실행 여부, 기본값 `true` | 아니오 | +| `GITO_SHELL_ID` | agent-shell 식별자, 기본값 `local-shell` | 아니오 | + +Credential 값은 tracked 파일에 쓰지 않는다. 필요한 경우 `credential_ref`나 ignored local secret file로 연결한다. + +## 참고 문서 + +- [docs/architecture.md](docs/architecture.md) +- [packages/contracts/notes/gito-control-plane.md](packages/contracts/notes/gito-control-plane.md) +- [agent-ops/skills/common/create-readme/SKILL.md](agent-ops/skills/common/create-readme/SKILL.md) diff --git a/agent-ops/.version b/agent-ops/.version new file mode 100644 index 0000000..5c7cd93 --- /dev/null +++ b/agent-ops/.version @@ -0,0 +1 @@ +1.1.136 diff --git a/agent-ops/bin/ai-ignore.sh b/agent-ops/bin/ai-ignore.sh new file mode 100755 index 0000000..964ba95 --- /dev/null +++ b/agent-ops/bin/ai-ignore.sh @@ -0,0 +1,268 @@ +#!/usr/bin/env bash + +# Shared AI ignore / permission defaults for init and sync flows. +AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN="agent-task/archive/**" +AGENT_ROADMAP_ARCHIVE_IGNORE_PATTERN="agent-roadmap/archive/**" +AGENT_ROADMAP_CURRENT_LOCAL_PATTERN="agent-roadmap/current.md" +AGENT_OPS_AI_IGNORE_BLOCK_BEGIN="# BEGIN Agent-Ops managed ignore" +AGENT_OPS_AI_IGNORE_BLOCK_END="# END Agent-Ops managed ignore" +AGENT_OPS_GITIGNORE_BLOCK_BEGIN="# BEGIN Agent-Ops managed gitignore" +AGENT_OPS_GITIGNORE_BLOCK_END="# END Agent-Ops managed gitignore" +AGENT_OPS_AI_IGNORE_FILES=(".geminiignore" ".aiexclude" ".cursorignore" ".clineignore") +AGENT_OPS_AI_PERMISSION_FILES=(".claude/settings.json" "opencode.json") +AGENT_OPS_AI_SYNC_FILES=(".gitignore" "${AGENT_OPS_AI_IGNORE_FILES[@]}" "${AGENT_OPS_AI_PERMISSION_FILES[@]}") + +agent_ops_ensure_gitignore_task_artifact_block() { + local file="$1" + local tmp + + touch "$file" + if grep -qxF "$AGENT_OPS_GITIGNORE_BLOCK_BEGIN" "$file" \ + && grep -qxF "$AGENT_OPS_GITIGNORE_BLOCK_END" "$file"; then + tmp="$(mktemp "$file.tmp.XXXXXX")" + awk \ + -v begin="$AGENT_OPS_GITIGNORE_BLOCK_BEGIN" \ + -v end="$AGENT_OPS_GITIGNORE_BLOCK_END" \ + -v current="$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" ' + $0 == begin { + print begin + print "!agent-task/" + print "!agent-task/**/" + print "!agent-task/**/*.md" + print "!agent-task/**/*.log" + print current + print end + in_block = 1 + next + } + $0 == end && in_block { + in_block = 0 + next + } + !in_block { print } + ' "$file" > "$tmp" + mv "$tmp" "$file" + else + if [[ -s "$file" ]]; then + printf "\n" >> "$file" + fi + printf "%s\n" "$AGENT_OPS_GITIGNORE_BLOCK_BEGIN" >> "$file" + printf "%s\n" "!agent-task/" >> "$file" + printf "%s\n" "!agent-task/**/" >> "$file" + printf "%s\n" "!agent-task/**/*.md" >> "$file" + printf "%s\n" "!agent-task/**/*.log" >> "$file" + printf "%s\n" "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" >> "$file" + printf "%s\n" "$AGENT_OPS_GITIGNORE_BLOCK_END" >> "$file" + fi +} + +agent_ops_ensure_ai_ignore_block() { + local file="$1" + local tmp + + touch "$file" + if grep -qxF "$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" "$file" \ + && grep -qxF "$AGENT_OPS_AI_IGNORE_BLOCK_END" "$file"; then + tmp="$(mktemp "$file.tmp.XXXXXX")" + awk \ + -v begin="$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" \ + -v end="$AGENT_OPS_AI_IGNORE_BLOCK_END" \ + -v task="$AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN" \ + -v roadmap="$AGENT_ROADMAP_ARCHIVE_IGNORE_PATTERN" ' + $0 == begin { + print begin + print task + print roadmap + print end + in_block = 1 + next + } + $0 == end && in_block { + in_block = 0 + next + } + !in_block { print } + ' "$file" > "$tmp" + mv "$tmp" "$file" + else + if [[ -s "$file" ]]; then + printf "\n" >> "$file" + fi + printf "%s\n" "$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" >> "$file" + printf "%s\n" "$AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN" >> "$file" + printf "%s\n" "$AGENT_ROADMAP_ARCHIVE_IGNORE_PATTERN" >> "$file" + printf "%s\n" "$AGENT_OPS_AI_IGNORE_BLOCK_END" >> "$file" + fi +} + +agent_ops_merge_json_with_jq() { + local file="$1" + local filter="$2" + local fallback_note="$3" + local tmp + + if ! command -v jq >/dev/null 2>&1; then + echo "$fallback_note" + return + fi + + tmp="$(mktemp "$file.tmp.XXXXXX")" + if jq "$filter" "$file" > "$tmp"; then + mv "$tmp" "$file" + else + rm -f "$tmp" + echo "$fallback_note" + fi +} + +agent_ops_merge_claude_settings() { + local file="$1" + local filter=' +def append_unique($item): + if index($item) then . else . + [$item] end; +def as_array: + if type == "array" then . elif . == null then [] else [.] end; +def as_object: + if type == "object" then . else {} end; + +.permissions = ((.permissions // {}) | as_object) +| .permissions.deny = ( + (.permissions.deny | as_array) + | map(select(. != "Read(./agent-roadmap/archive/**)")) + | append_unique("Read(./agent-task/archive/**)") + ) +' + + agent_ops_merge_json_with_jq \ + "$file" \ + "$filter" \ + "Note: .claude/settings.json exists; add Read(./agent-task/archive/**) and remove Read(./agent-roadmap/archive/**) manually." +} + +agent_ops_claude_settings_complete() { + local file="$1" + local filter=' +def as_array: + if type == "array" then . elif . == null then [] else [.] end; + +(.permissions.deny | as_array) +as $deny +| (($deny | index("Read(./agent-task/archive/**)")) != null) +and (($deny | index("Read(./agent-roadmap/archive/**)")) == null) +' + + if command -v jq >/dev/null 2>&1; then + jq -e "$filter" "$file" >/dev/null 2>&1 + else + grep -q "agent-task/archive" "$file" + fi +} + +agent_ops_merge_opencode_config() { + local file="$1" + local filter=' +def append_unique($item): + if index($item) then . else . + [$item] end; +def as_array: + if type == "array" then . elif . == null then [] else [.] end; +def as_object: + if type == "object" then . else {} end; + +.permission = ((.permission // {}) | as_object) +| .permission.read = ((.permission.read // {}) | as_object) +| .permission.read["agent-task/archive/**"] = "deny" +| del(.permission.read["agent-roadmap/archive/**"]) +| .permission.glob = ((.permission.glob // {}) | as_object) +| .permission.glob["agent-task/archive/**"] = "deny" +| del(.permission.glob["agent-roadmap/archive/**"]) +| .watcher = ((.watcher // {}) | as_object) +| .watcher.ignore = ( + (.watcher.ignore | as_array) + | append_unique("agent-task/archive/**") + | append_unique("agent-roadmap/archive/**") + ) +' + + agent_ops_merge_json_with_jq \ + "$file" \ + "$filter" \ + "Note: opencode.json exists; add agent-task/archive/** read/glob deny, remove agent-roadmap/archive/** read/glob hard deny, and add agent-task/archive/** plus agent-roadmap/archive/** watcher ignore manually." +} + +agent_ops_opencode_config_complete() { + local file="$1" + local filter=' +def as_array: + if type == "array" then . elif . == null then [] else [.] end; + +(.permission.read["agent-task/archive/**"] == "deny") +and (.permission.glob["agent-task/archive/**"] == "deny") +and (.permission.read["agent-roadmap/archive/**"] != "deny") +and (.permission.glob["agent-roadmap/archive/**"] != "deny") +and ((.watcher.ignore | as_array) as $ignore | (($ignore | index("agent-task/archive/**")) != null) and (($ignore | index("agent-roadmap/archive/**")) != null)) +' + + if command -v jq >/dev/null 2>&1; then + jq -e "$filter" "$file" >/dev/null 2>&1 + else + grep -q "agent-task/archive" "$file" && grep -q "agent-roadmap/archive" "$file" + fi +} + +ensure_agent_ops_ai_ignore_config() { + local target_dir="$1" + local ignore_file + + if [[ -f "$target_dir/.agent-ops-source" ]]; then + echo " AI ignore 보강 건너뜀: .agent-ops-source repo" + return + fi + + agent_ops_ensure_gitignore_task_artifact_block "$target_dir/.gitignore" + + for ignore_file in "${AGENT_OPS_AI_IGNORE_FILES[@]}"; do + agent_ops_ensure_ai_ignore_block "$target_dir/$ignore_file" + done + + mkdir -p "$target_dir/.claude" + if [[ ! -f "$target_dir/.claude/settings.json" ]]; then + cat > "$target_dir/.claude/settings.json" <<'EOF' +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "deny": [ + "Read(./agent-task/archive/**)" + ] + } +} +EOF + elif ! agent_ops_claude_settings_complete "$target_dir/.claude/settings.json"; then + agent_ops_merge_claude_settings "$target_dir/.claude/settings.json" + fi + + if [[ ! -f "$target_dir/opencode.json" ]]; then + cat > "$target_dir/opencode.json" <<'EOF' +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "read": { + "agent-task/archive/**": "deny" + }, + "glob": { + "agent-task/archive/**": "deny" + } + }, + "watcher": { + "ignore": [ + "agent-task/archive/**", + "agent-roadmap/archive/**" + ] + } +} +EOF + elif ! agent_ops_opencode_config_complete "$target_dir/opencode.json"; then + agent_ops_merge_opencode_config "$target_dir/opencode.json" + fi + + echo " AI permission 표준 적용: roadmap archive hard deny 제거" +} diff --git a/agent-ops/bin/bump-version.sh b/agent-ops/bin/bump-version.sh new file mode 100755 index 0000000..4cc50ef --- /dev/null +++ b/agent-ops/bin/bump-version.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# bump-version.sh +# patch +1, 999 초과 시 minor 올림. major는 수동 관리. +# 결과 버전을 stdout으로 출력. + +set -euo pipefail + +YELLOW='\033[1;33m'; RESET='\033[0m' + +VERSION="${1:-}" +if [[ -z "$VERSION" ]]; then + echo "사용법: $0 " >&2 + exit 1 +fi + +IFS='.' read -r major minor patch <<< "$VERSION" + +patch=$((patch + 1)) +if [[ $patch -gt 999 ]]; then + patch=0 + minor=$((minor + 1)) +fi +if [[ $minor -gt 999 ]]; then + minor=0 + echo -e "${YELLOW}⚠ minor 버전이 999를 초과했습니다. major 버전을 수동으로 올려주세요.${RESET}" >&2 +fi + +echo "$major.$minor.$patch" diff --git a/agent-ops/bin/entry-files.sh b/agent-ops/bin/entry-files.sh new file mode 100644 index 0000000..dfb6f90 --- /dev/null +++ b/agent-ops/bin/entry-files.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Shared entry-point file list for init/sync scripts. +AGENT_OPS_ENTRY_FILES=("GEMINI.md" "CLAUDE.md" "AGENTS.md" ".cursorrules" ".clinerules") + +apply_agent_ops_entry_files() { + local rules_md="$1" + local target_root="$2" + + if [[ ! -f "$rules_md" ]]; then + if [[ -n "${YELLOW:-}" && -n "${RESET:-}" ]]; then + echo -e "${YELLOW} rules.md 없음, 진입 파일 재적용 건너뜀${RESET}" + else + echo " rules.md 없음, 진입 파일 재적용 건너뜀" + fi + return + fi + + local f + for f in "${AGENT_OPS_ENTRY_FILES[@]}"; do + cp "$rules_md" "$target_root/$f" + echo " 진입 파일 적용: $f" + done +} diff --git a/agent-ops/bin/init-agent-ops.sh b/agent-ops/bin/init-agent-ops.sh new file mode 100755 index 0000000..765683a --- /dev/null +++ b/agent-ops/bin/init-agent-ops.sh @@ -0,0 +1,343 @@ +#!/bin/bash + +# agent-ops/bin/init-agent-ops.sh +# 프로젝트에 agent-ops 스캐폴드를 초기화하는 스크립트 + +set -e + +SCRIPT_DIR=$(realpath "$(dirname "$0")") +SOURCE_DIR=$(realpath "$SCRIPT_DIR/..") +source "$SCRIPT_DIR/entry-files.sh" +source "$SCRIPT_DIR/ai-ignore.sh" + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +TARGET_DIR=$(realpath "$1") + +create_project_agent_ops_dirs() { + local agent_ops_dir="$1" + + mkdir -p "$agent_ops_dir/rules/project/domain" + mkdir -p "$agent_ops_dir/rules/private" + mkdir -p "$agent_ops_dir/skills/project" +} + +copy_common_agent_ops() { + local source_dir="$1" + local target_agent_ops_dir="$2" + + mkdir -p "$target_agent_ops_dir/rules" + mkdir -p "$target_agent_ops_dir/skills" + + cp "$source_dir/.version" "$target_agent_ops_dir/" + rm -rf "$target_agent_ops_dir/bin" + rm -rf "$target_agent_ops_dir/rules/common" + rm -rf "$target_agent_ops_dir/skills/common" + cp -r "$source_dir/bin" "$target_agent_ops_dir/" + cp -r "$source_dir/rules/common" "$target_agent_ops_dir/rules/" + cp -r "$source_dir/skills/common" "$target_agent_ops_dir/skills/" +} + +ensure_common_rules_file() { + local source_dir="$1" + local target_agent_ops_dir="$2" + local source_rules="$source_dir/rules/common/rules.md" + local target_rules="$target_agent_ops_dir/rules/common/rules.md" + + if [ ! -f "$source_rules" ]; then + echo "Error: common rules file not found: $source_rules" >&2 + exit 1 + fi + + mkdir -p "$(dirname "$target_rules")" + cp "$source_rules" "$target_rules" +} + +ensure_gitignore_entry() { + local gitignore_file="$1" + local entry="$2" + + if ! grep -qxF "$entry" "$gitignore_file"; then + printf '%s\n' "$entry" >> "$gitignore_file" + fi +} + +discover_agent_test_domains() { + local target_dir="$1" + local domain_root="$target_dir/agent-ops/rules/project/domain" + local project_rules="$target_dir/agent-ops/rules/project/rules.md" + + if [ -d "$domain_root" ]; then + find "$domain_root" -mindepth 2 -maxdepth 2 -name rules.md -print \ + | while IFS= read -r rule_file; do + basename "$(dirname "$rule_file")" + done + fi + + if [ -f "$project_rules" ]; then + grep -Eo 'agent-ops/rules/project/domain/[^/`[:space:]]+/rules\.md' "$project_rules" \ + | awk -F/ '{ print $(NF-1) }' + fi +} + +ensure_agent_test_route() { + local rules_file="$1" + local route_line="$2" + local placeholder="- 현재 등록된 도메인/검증 시나리오별 테스트 문서 없음." + local tmp + + if grep -qxF -- "$route_line" "$rules_file"; then + return + fi + + tmp="$(mktemp "$rules_file.tmp.XXXXXX")" + if grep -qxF -- "$placeholder" "$rules_file"; then + awk -v placeholder="$placeholder" -v route_line="$route_line" ' + $0 == placeholder { print route_line; next } + { print } + ' "$rules_file" > "$tmp" + else + awk -v route_line="$route_line" ' + BEGIN { added = 0 } + { print } + !added && $0 == "## 라우팅" { + print "" + print route_line + added = 1 + } + END { + if (!added) { + print "" + print "## 라우팅" + print "" + print route_line + } + } + ' "$rules_file" > "$tmp" + fi + mv "$tmp" "$rules_file" +} + +ensure_agent_test_profile() { + local target_dir="$1" + local env="$2" + local profile="$3" + local domain="$4" + local verification_type="$5" + local scope="$6" + local today="$7" + local profile_file="$target_dir/agent-test/$env/$profile.md" + local rules_file="$target_dir/agent-test/$env/rules.md" + local route_line + + mkdir -p "$(dirname "$profile_file")" + + if [ ! -f "$profile_file" ]; then + cat > "$profile_file" < + +## 분류 + +- domain: $domain +- verification_type: $verification_type +- scope: $scope + +## 환경 + +- host: +- port: +- runtime: +- package manager: +- docker: +- external service: +- model endpoint: +- credential: + +## 명령 + +- setup: +- lint: +- unit: +- smoke: +- e2e: +- model: +- full-cycle: + +## 필수 검증 + +- <확인 필요> + +## 보조 검증 + +- <확인 필요> + +## 판정 기준 + +- <확인 필요> + +## 기준 출력 예시 + +\`\`\`text +<필요한 경우 기대 출력 예시> +\`\`\` + +## 차단 기준 + +- <확인 필요> + +## 보고 항목 + +- 실행한 명령: +- 성공한 검증: +- 실패/차단된 검증: +- 생략 사유: +- 남은 위험: + +## 금지 사항 + +- 확인되지 않은 host, port, token, endpoint를 추측해 쓰지 않는다. +- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다. +EOF + fi + + route_line="- $domain / $verification_type / $scope: \`agent-test/$env/$profile.md\`" + ensure_agent_test_route "$rules_file" "$route_line" +} + +ensure_agent_test_local() { + local target_dir="$1" + local local_rules="$target_dir/agent-test/local/rules.md" + local today + local domain + local created_domain_profile="0" + + today="$(date +%F)" + mkdir -p "$(dirname "$local_rules")" + mkdir -p "$target_dir/agent-test/runs" + + if [ ! -f "$local_rules" ]; then + cat > "$local_rules" <-smoke 문서를 기본 baseline으로 둔다. +- 도메인이 아직 없을 때만 project-smoke를 fallback baseline으로 둔다. +- 도메인/검증 시나리오별 문서는 다른 테스트 문서로 라우팅하지 않는다. +EOF + fi + + while IFS= read -r domain; do + [ -n "$domain" ] || continue + created_domain_profile="1" + ensure_agent_test_profile \ + "$target_dir" \ + "local" \ + "$domain-smoke" \ + "$domain" \ + "smoke" \ + "도메인 기본 smoke 검증" \ + "$today" + done < <(discover_agent_test_domains "$target_dir" | sort -u) + + if [ "$created_domain_profile" = "0" ]; then + ensure_agent_test_profile \ + "$target_dir" \ + "local" \ + "project-smoke" \ + "project-wide" \ + "smoke" \ + "프로젝트 기본 smoke 검증" \ + "$today" + fi +} + +echo "Initializing agent-ops in: $TARGET_DIR" +echo "Source agent-ops: $SOURCE_DIR" + +# 1. 대상 폴더 생성 (프로젝트 전용 폴더는 내용 복사 없이 폴더만 생성) +create_project_agent_ops_dirs "$TARGET_DIR/agent-ops" + +# 2. 공통 요소 복사 (프로젝트 전용 설정은 제외) +copy_common_agent_ops "$SOURCE_DIR" "$TARGET_DIR/agent-ops" + +# 3. 에이전트 진입 파일 생성 (common/rules.md 복사) +COMMON_RULES="$SOURCE_DIR/rules/common/rules.md" +apply_agent_ops_entry_files "$COMMON_RULES" "$TARGET_DIR" +ensure_common_rules_file "$SOURCE_DIR" "$TARGET_DIR/agent-ops" + +# 4. AI ignore / permission 설정 +ensure_agent_ops_ai_ignore_config "$TARGET_DIR" + +# 5. .gitignore 설정 +TOUCH_GITIGNORE="$TARGET_DIR/.gitignore" +touch "$TOUCH_GITIGNORE" +agent_ops_ensure_gitignore_task_artifact_block "$TOUCH_GITIGNORE" +if ! grep -q "agent-ops/rules/private/" "$TOUCH_GITIGNORE"; then + echo "" >> "$TOUCH_GITIGNORE" + echo "# Agent-Ops Private Rules" >> "$TOUCH_GITIGNORE" + echo "agent-ops/rules/private/" >> "$TOUCH_GITIGNORE" +fi +if ! grep -qxF "# Agent-Test Local Environment" "$TOUCH_GITIGNORE"; then + echo "" >> "$TOUCH_GITIGNORE" + echo "# Agent-Test Local Environment" >> "$TOUCH_GITIGNORE" +fi +ensure_gitignore_entry "$TOUCH_GITIGNORE" "agent-test/local/" +ensure_gitignore_entry "$TOUCH_GITIGNORE" "agent-test/runs/" + +# 6. local 테스트 환경 생성 +ensure_agent_test_local "$TARGET_DIR" + +echo "Successfully initialized agent-ops in $TARGET_DIR" +echo "Note: agent-ops/rules/project and agent-ops/skills/project are initialized as empty." +echo "Note: agent-test/local rules and baseline test profiles are initialized and ignored by git." diff --git a/agent-ops/bin/roadmap-dependency-checker.sh b/agent-ops/bin/roadmap-dependency-checker.sh new file mode 100755 index 0000000..71a3dfc --- /dev/null +++ b/agent-ops/bin/roadmap-dependency-checker.sh @@ -0,0 +1,370 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AGENT_OPS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +PROJECT_ROOT="$(cd "$AGENT_OPS_DIR/.." && pwd)" +WORKSPACE_ROOT="$(cd "$PROJECT_ROOT/.." && pwd)" +LOCKS_FILE="${AGENT_ROADMAP_LOCKS_FILE:-$WORKSPACE_ROOT/.agent-roadmap-sync/locks.yaml}" + +usage() { + cat <<'EOF' +Usage: roadmap-dependency-checker.sh [locks.yaml] + roadmap-dependency-checker.sh --find-milestone [locked|rely-on|both] [locks.yaml] + +Validates locked, rely-on.target, and rely-on.status for . +Prints true when every rely-on.status is enable. +Prints false when at least one rely-on.status is disable. +With --find-milestone, prints matching lock ids as locked: or rely-on:, +or none when the milestone identity is not referenced. + +Exit codes: + 0 all dependencies are enabled + 1 at least one dependency is still disabled + 2 configuration, input, or parse error +For --find-milestone, exit code 0 means the lookup completed; stdout is either +matching entries or none. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +MODE="check" +LOCK_ID="" +FIND_IDENTITY="" +FIND_DIRECTION="both" + +if [[ "${1:-}" == "--find-milestone" ]]; then + MODE="find" + if [[ $# -lt 2 || $# -gt 4 ]]; then + usage >&2 + exit 2 + fi + FIND_IDENTITY="$2" + if [[ -z "$FIND_IDENTITY" ]]; then + echo "error: milestone identity is required" >&2 + exit 2 + fi + if [[ $# -ge 3 ]]; then + case "$3" in + locked|rely-on|both) + FIND_DIRECTION="$3" + if [[ $# -eq 4 ]]; then + LOCKS_FILE="$4" + fi + ;; + *) + if [[ $# -eq 3 ]]; then + LOCKS_FILE="$3" + else + usage >&2 + exit 2 + fi + ;; + esac + fi +elif [[ $# -lt 1 || $# -gt 2 ]]; then + usage >&2 + exit 2 +else + LOCK_ID="$1" + if [[ -n "${2:-}" ]]; then + LOCKS_FILE="$2" + fi +fi + +if [[ "$MODE" == "check" && -z "$LOCK_ID" ]]; then + echo "error: lock id is required" >&2 + exit 2 +fi + +if [[ ! -r "$LOCKS_FILE" ]]; then + echo "error: locks file not readable: $LOCKS_FILE" >&2 + exit 2 +fi + +if [[ "$MODE" == "find" ]]; then + awk -v identity="$FIND_IDENTITY" -v direction="$FIND_DIRECTION" ' +function trim(value) { + gsub(/\r/, "", value) + sub(/^[[:space:]]+/, "", value) + sub(/[[:space:]]+$/, "", value) + return value +} + +function unquote(value) { + value = trim(value) + sub(/[[:space:]]+#.*$/, "", value) + value = trim(value) + if (value ~ /^".*"$/ || value ~ /^'\''.*'\''$/) { + return substr(value, 2, length(value) - 2) + } + return trim(value) +} + +function reset_entry() { + entry_id = "" + locked = "" + in_rely_on = 0 + rely_count = 0 + delete rely_targets +} + +function finish_entry( i) { + if (entry_id == "") { + return + } + if ((direction == "both" || direction == "locked") && locked == identity) { + print "locked:" entry_id + found = 1 + } + if (direction == "both" || direction == "rely-on") { + for (i = 1; i <= rely_count; i++) { + if (rely_targets[i] == identity) { + print "rely-on:" entry_id + found = 1 + break + } + } + } +} + +BEGIN { + found = 0 + reset_entry() +} + +/^[[:space:]]*($|#)/ { + next +} + +/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/ { + finish_entry() + reset_entry() + value = $0 + sub(/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/, "", value) + entry_id = unquote(value) + next +} + +entry_id != "" && /^[[:space:]]*locked:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*locked:[[:space:]]*/, "", value) + locked = unquote(value) + next +} + +entry_id != "" && /^[[:space:]]*rely-on:[[:space:]]*/ { + in_rely_on = 1 + next +} + +entry_id != "" && in_rely_on && /^[[:space:]]*-[[:space:]]*target:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*-[[:space:]]*target:[[:space:]]*/, "", value) + rely_targets[++rely_count] = unquote(value) + next +} + +entry_id != "" && in_rely_on && /^[[:space:]]*target:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*target:[[:space:]]*/, "", value) + rely_targets[++rely_count] = unquote(value) + next +} + +END { + finish_entry() + if (!found) { + print "none" + } +} +' "$LOCKS_FILE" + exit 0 +fi + +awk -v wanted="$LOCK_ID" ' +function trim(value) { + gsub(/\r/, "", value) + sub(/^[[:space:]]+/, "", value) + sub(/[[:space:]]+$/, "", value) + return value +} + +function unquote(value) { + value = trim(value) + sub(/[[:space:]]+#.*$/, "", value) + value = trim(value) + if (value ~ /^".*"$/ || value ~ /^'\''.*'\''$/) { + return substr(value, 2, length(value) - 2) + } + return trim(value) +} + +function fail(message) { + error = message + printf "error: %s\n", error > "/dev/stderr" + exit 2 +} + +function finish_dependency() { + if (!selected || !in_dependency) { + return + } + if (!dependency_has_target) { + fail("missing rely-on.target for lock id: " wanted) + } + if (!dependency_has_status) { + fail("missing rely-on.status for lock id: " wanted) + } + in_dependency = 0 +} + +function finish_entry() { + if (!selected) { + return + } + finish_dependency() + if (!entry_has_locked) { + fail("missing locked for lock id: " wanted) + } + if (total == 0) { + fail("no rely-on.status entries for lock id: " wanted) + } + selected = 0 +} + +BEGIN { + selected = 0 + matches = 0 + total = 0 + enabled = 0 + error = "" + entry_has_locked = 0 + in_rely_on = 0 + in_dependency = 0 + dependency_has_target = 0 + dependency_has_status = 0 +} + +/^[[:space:]]*($|#)/ { + next +} + +/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/ { + finish_entry() + value = $0 + sub(/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/, "", value) + id = unquote(value) + if (id == wanted) { + if (matches > 0) { + fail("duplicate lock id: " wanted) + } + selected = 1 + matches++ + total = 0 + enabled = 0 + entry_has_locked = 0 + in_rely_on = 0 + in_dependency = 0 + dependency_has_target = 0 + dependency_has_status = 0 + } + next +} + +selected && /^[[:space:]]*locked:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*locked:[[:space:]]*/, "", value) + locked = unquote(value) + if (locked == "") { + fail("empty locked for lock id: " wanted) + } + entry_has_locked = 1 + next +} + +selected && /^[[:space:]]*rely-on:[[:space:]]*/ { + in_rely_on = 1 + next +} + +selected && in_rely_on && /^[[:space:]]*-[[:space:]]*target:[[:space:]]*/ { + finish_dependency() + in_dependency = 1 + dependency_has_target = 0 + dependency_has_status = 0 + value = $0 + sub(/^[[:space:]]*-[[:space:]]*target:[[:space:]]*/, "", value) + target = unquote(value) + if (target == "") { + fail("empty rely-on.target for lock id: " wanted) + } + dependency_has_target = 1 + next +} + +selected && in_rely_on && /^[[:space:]]*target:[[:space:]]*/ { + if (!in_dependency) { + fail("rely-on.target outside dependency item for lock id: " wanted) + } + value = $0 + sub(/^[[:space:]]*target:[[:space:]]*/, "", value) + target = unquote(value) + if (target == "") { + fail("empty rely-on.target for lock id: " wanted) + } + dependency_has_target = 1 + next +} + +selected && in_rely_on && /^[[:space:]]*status:[[:space:]]*/ { + if (!in_dependency) { + fail("rely-on.status outside dependency item for lock id: " wanted) + } + value = $0 + sub(/^[[:space:]]*status:[[:space:]]*/, "", value) + status = unquote(value) + if (status != "enable" && status != "disable") { + fail("invalid rely-on.status for " wanted ": " status) + } + if (dependency_has_status) { + fail("duplicate rely-on.status for lock id: " wanted) + } + dependency_has_status = 1 + total++ + if (status == "enable") { + enabled++ + } + next +} + +selected && in_rely_on && /^[[:space:]]*-[[:space:]]*/ { + finish_dependency() + fail("missing rely-on.target for lock id: " wanted) +} + +selected && /^[[:space:]]*status:[[:space:]]*/ { + fail("status outside rely-on for lock id: " wanted) +} + +END { + if (error != "") { + exit 2 + } + if (matches == 0) { + printf "error: lock id not found: %s\n", wanted > "/dev/stderr" + exit 2 + } + finish_entry() + if (total == enabled) { + print "true" + exit 0 + } + print "false" + exit 1 +} +' "$LOCKS_FILE" diff --git a/agent-ops/bin/sync.sh b/agent-ops/bin/sync.sh new file mode 100755 index 0000000..21b7eca --- /dev/null +++ b/agent-ops/bin/sync.sh @@ -0,0 +1,338 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ── 경로 설정 ──────────────────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AGENT_OPS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +PROJECT_ROOT="$(cd "$AGENT_OPS_DIR/.." && pwd)" + +# ── 색상 ───────────────────────────────────────────────────────────────────── +RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; RESET='\033[0m' + +# ── 진입 파일 공통 관리 ────────────────────────────────────────────────────── +source "$SCRIPT_DIR/entry-files.sh" +source "$SCRIPT_DIR/ai-ignore.sh" + +# ── 대상 경로 해석 (폴더명 / 상대경로 / 절대경로) ──────────────────────────── +resolve_target() { + local input="$1" + if [[ "$input" == /* ]]; then + [[ -d "$input" ]] && echo "$input" || echo "" + return + fi + if [[ "$input" == */* ]]; then + local resolved + resolved="$(cd "$input" 2>/dev/null && pwd)" || { echo ""; return; } + echo "$resolved" + return + fi + local sibling="$(dirname "$PROJECT_ROOT")/$input" + [[ -d "$sibling" ]] && echo "$sibling" || echo "" +} + +# ── 버전 비교: v1 > v2 이면 0 반환 ─────────────────────────────────────────── +version_gt() { + local v1="$1" v2="$2" + [[ "$v1" == "$v2" ]] && return 1 + [[ "$(printf '%s\n%s' "$v1" "$v2" | sort -V | head -1)" == "$v2" ]] +} + +# ── 버전 +1 ────────────────────────────────────────────────────────────────── +bump_version() { + bash "$SCRIPT_DIR/bump-version.sh" "$1" +} + +# ── 폴더 동기화 (삭제된 파일도 반영) ──────────────────────────────────────── +sync_folder() { + local src="$1" dst="$2" exclude="${3:-}" + mkdir -p "$dst" + # dst에서 src에 없는 항목 제거 (exclude 파일 보존) + find "$dst" -mindepth 1 -maxdepth 1 | while IFS= read -r item; do + local name + name="$(basename "$item")" + [[ -n "$exclude" && "$name" == "$exclude" ]] && continue + if [[ ! -e "$src/$name" ]]; then + rm -rf "$item" + fi + done + # src에서 dst로 복사 (exclude 파일 제외) + find "$src" -mindepth 1 -maxdepth 1 | while IFS= read -r item; do + local name + name="$(basename "$item")" + [[ -n "$exclude" && "$name" == "$exclude" ]] && continue + rm -rf "$dst/$name" + cp -r "$item" "$dst/" + done +} + +sync_common() { + local src="$1" dst="$2" + sync_folder "$src/rules/common" "$dst/rules/common" + sync_folder "$src/skills/common" "$dst/skills/common" + sync_folder "$src/bin" "$dst/bin" +} + +common_differs() { + local src="$1" dst="$2" + local path + + for path in "rules/common" "skills/common" "bin"; do + if [[ ! -d "$src/$path" || ! -d "$dst/$path" ]]; then + return 0 + fi + if ! diff -qr "$src/$path" "$dst/$path" >/dev/null; then + return 0 + fi + done + + return 1 +} + +create_project_agent_ops_dirs() { + local agent_ops_dir="$1" + mkdir -p "$agent_ops_dir/rules/project/domain" + mkdir -p "$agent_ops_dir/rules/private" + mkdir -p "$agent_ops_dir/skills/project" +} + +copy_common_scaffold() { + local src="$1" dst="$2" + mkdir -p "$dst/rules" "$dst/skills" + cp "$src/.version" "$dst/" + rm -rf "$dst/bin" "$dst/rules/common" "$dst/skills/common" + cp -r "$src/bin" "$dst/" + cp -r "$src/rules/common" "$dst/rules/" + cp -r "$src/skills/common" "$dst/skills/" + create_project_agent_ops_dirs "$dst" +} + +discover_agent_ops_targets() { + local parent + parent="$(dirname "$PROJECT_ROOT")" + + find "$parent" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r candidate; do + local resolved + resolved="$(cd "$candidate" && pwd)" + [[ "$resolved" == "$PROJECT_ROOT" ]] && continue + [[ -d "$resolved/agent-ops" ]] || continue + echo "$resolved" + done +} + +agent_ops_git_paths() { + local include_ai_config="${1:-1}" + + printf '%s\n' "agent-ops/.version" + printf '%s\n' "agent-ops/bin" + printf '%s\n' "agent-ops/rules/common" + printf '%s\n' "agent-ops/skills/common" + + local f + for f in "${AGENT_OPS_ENTRY_FILES[@]}"; do + if [[ -e "$f" ]] || git ls-files --error-unmatch "$f" >/dev/null 2>&1; then + printf '%s\n' "$f" + fi + done + if [[ "$include_ai_config" != "1" || -f ".agent-ops-source" ]]; then + return + fi + for f in "${AGENT_OPS_AI_SYNC_FILES[@]}"; do + if [[ -e "$f" ]] || git ls-files --error-unmatch "$f" >/dev/null 2>&1; then + printf '%s\n' "$f" + fi + done +} + +commit_and_push_agent_ops_scope() { + local repo="$1" message="$2" + local include_ai_config="${3:-1}" + + if ! git -C "$repo" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo -e "${RED}Error: git 저장소가 아닙니다: $repo${RESET}" + return 1 + fi + + ( + cd "$repo" + mapfile -t paths < <(agent_ops_git_paths "$include_ai_config") + git add -- "${paths[@]}" + if git ls-files --error-unmatch "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" >/dev/null 2>&1 \ + && grep -qxF "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" ".gitignore" 2>/dev/null; then + git rm --cached --quiet --ignore-unmatch "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" + paths+=("$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN") + fi + + if git diff --cached --quiet -- "${paths[@]}"; then + echo " agent-ops 변경 없음: commit 건너뜀" + else + git commit -m "$message" -- "${paths[@]}" + fi + + git push + ) +} + +sync_framework_to_target() { + local target="$1" + local dst_ao="$target/agent-ops" + local src_ver + src_ver="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" + + echo "▶ $(basename "$PROJECT_ROOT") → $(basename "$target")" + + if ! git -C "$target" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo -e "${RED}Error: git 저장소가 아닙니다: $target${RESET}" + return 1 + fi + + if [[ ! -d "$dst_ao" ]]; then + echo " 최초 진행: agent-ops 공통 scaffold 복사" + copy_common_scaffold "$SRC_AO" "$dst_ao" + apply_agent_ops_entry_files "$SRC_AO/rules/common/rules.md" "$target" + echo -e "${YELLOW} init-agent-ops 스킬로 초기화를 진행하세요.${RESET}" + else + echo " 이후 진행: common/ 동기화" + sync_common "$SRC_AO" "$dst_ao" + cp "$SRC_AO/.version" "$dst_ao/.version" + apply_agent_ops_entry_files "$SRC_AO/rules/common/rules.md" "$target" + fi + ensure_agent_ops_ai_ignore_config "$target" + + commit_and_push_agent_ops_scope "$target" "sync: agent-ops from $(basename "$PROJECT_ROOT") v$src_ver" + echo -e "${GREEN}✓ 완료 → $(basename "$target")${RESET}" +} + +# ── 사용법 ──────────────────────────────────────────────────────────────────── +usage() { + echo "사용법: $0 [--pull] [target]" + echo "" + echo " (기본) push 동기화" + echo " --pull target(공통 원본 repo, 기본 agentic-framework) → 현재 프로젝트 로 pull" + echo "" + echo " target: 폴더명 (동일 레벨 탐색) | 상대경로 | 절대경로" + echo " .agent-ops-source 원본 repo에서 target 생략 시 상위 폴더의 agent-ops 적용 프로젝트 전체 동기화" +} + +# ───────────────────────────────────────────────────────────────────────────── +PULL_MODE="0" +if [[ "${1:-}" == "--pull" ]]; then + PULL_MODE="1" + shift +fi + +TARGET_INPUT="${1:-}" + +SRC_AO="$AGENT_OPS_DIR" +IS_FRAMEWORK="$([[ -f "$PROJECT_ROOT/.agent-ops-source" ]] && echo "1" || echo "0")" + +# ── pull 모드: .agent-ops-source 원본 repo → 현재 프로젝트 ─────────────────── +if [[ "$PULL_MODE" == "1" ]]; then + if [[ -z "$TARGET_INPUT" ]]; then + usage; exit 1 + fi + TARGET="$(resolve_target "$TARGET_INPUT")" + if [[ -z "$TARGET" ]]; then + echo -e "${RED}Error: 대상을 찾을 수 없습니다: $TARGET_INPUT${RESET}" + exit 1 + fi + DST_AO="$TARGET/agent-ops" + + if [[ "$IS_FRAMEWORK" == "1" ]]; then + echo -e "${RED}Error: .agent-ops-source가 있는 공통 원본 repo에서는 --pull을 사용할 수 없습니다.${RESET}" + exit 1 + fi + if [[ ! -f "$TARGET/.agent-ops-source" ]]; then + echo -e "${RED}Error: target이 공통 원본 repo가 아닙니다 (.agent-ops-source 없음)${RESET}" + exit 1 + fi + SRC_VER="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" + DST_VER="$(cat "$DST_AO/.version" 2>/dev/null || echo "0.0.0")" + echo "▶ $(basename "$TARGET") → $(basename "$PROJECT_ROOT") (pull)" + echo " 현재 버전: $SRC_VER | 원본 버전: $DST_VER" + sync_common "$DST_AO" "$SRC_AO" + cp "$DST_AO/.version" "$SRC_AO/.version" + apply_agent_ops_entry_files "$DST_AO/rules/common/rules.md" "$PROJECT_ROOT" + ensure_agent_ops_ai_ignore_config "$PROJECT_ROOT" + commit_and_push_agent_ops_scope "$PROJECT_ROOT" "sync: pull from agentic-framework v$DST_VER" + echo -e "${GREEN}✓ 완료 (pull v$DST_VER)${RESET}" + exit 0 +fi + +# ── .agent-ops-source 원본 repo에서 다른 프로젝트로 ────────────────────────── +if [[ "$IS_FRAMEWORK" == "1" ]]; then + SRC_VER="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" + echo " .agent-ops-source 감지: push 동기화에서는 버전 bump 없이 v$SRC_VER 그대로 전파" + if [[ -n "$TARGET_INPUT" ]]; then + TARGET="$(resolve_target "$TARGET_INPUT")" + if [[ -z "$TARGET" ]]; then + echo -e "${RED}Error: 대상을 찾을 수 없습니다: $TARGET_INPUT${RESET}" + exit 1 + fi + sync_framework_to_target "$TARGET" + else + mapfile -t TARGETS < <(discover_agent_ops_targets) + if [[ "${#TARGETS[@]}" -eq 0 ]]; then + echo -e "${YELLOW}agent-ops가 적용된 sibling 프로젝트가 없습니다.${RESET}" + exit 0 + fi + + STATUS=0 + for TARGET in "${TARGETS[@]}"; do + if ! sync_framework_to_target "$TARGET"; then + STATUS=1 + fi + done + exit "$STATUS" + fi + exit 0 +fi + +# ── 일반 프로젝트에서 .agent-ops-source 원본 repo로 (push) ─────────────────── +if [[ -z "$TARGET_INPUT" ]]; then + TARGET_INPUT="agentic-framework" +fi + +TARGET="$(resolve_target "$TARGET_INPUT")" +if [[ -z "$TARGET" ]]; then + echo -e "${RED}Error: 대상을 찾을 수 없습니다: $TARGET_INPUT${RESET}" + exit 1 +fi +DST_AO="$TARGET/agent-ops" + +echo "▶ $(basename "$PROJECT_ROOT") → $(basename "$TARGET")" + +if [[ ! -f "$TARGET/.agent-ops-source" ]]; then + echo -e "${RED}Error: target이 공통 원본 repo가 아닙니다 (.agent-ops-source 없음)${RESET}" + exit 1 +fi + +SRC_VER="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" +DST_VER="$(cat "$DST_AO/.version" 2>/dev/null || echo "0.0.0")" +echo " 현재 버전: $SRC_VER | 원본 버전: $DST_VER" + +COMMON_CHANGED="1" +if ! common_differs "$SRC_AO" "$DST_AO"; then + COMMON_CHANGED="0" +fi + +if [[ "$COMMON_CHANGED" == "1" ]] && version_gt "$DST_VER" "$SRC_VER"; then + echo -e "${RED}⚠ 버전 충돌: 원본 repo($DST_VER) > current($SRC_VER)${RESET}" + echo -e "${YELLOW} 먼저 sync-pull로 내려받은 뒤 재시도하세요.${RESET}" + exit 1 +fi + +if [[ "$COMMON_CHANGED" == "0" ]]; then + echo " 공통 파일 변경 없음: .version 차이는 무시하고 버전 갱신/커밋을 건너뜀" + echo -e "${GREEN}✓ 완료 (공통 변경 없음)${RESET}" + exit 0 +fi + +NEW_VER="$(bump_version "$SRC_VER")" +sync_common "$SRC_AO" "$DST_AO" +echo "$NEW_VER" > "$SRC_AO/.version" +echo "$NEW_VER" > "$DST_AO/.version" + +commit_and_push_agent_ops_scope "$TARGET" "sync: from $(basename "$PROJECT_ROOT") v$NEW_VER" "0" +commit_and_push_agent_ops_scope "$PROJECT_ROOT" "sync: to agentic-framework v$NEW_VER" "0" + +echo -e "${GREEN}✓ 완료 (v$SRC_VER → v$NEW_VER)${RESET}" diff --git a/agent-ops/rules/common/_templates/domain-rule-template.md b/agent-ops/rules/common/_templates/domain-rule-template.md new file mode 100644 index 0000000..a2ea36f --- /dev/null +++ b/agent-ops/rules/common/_templates/domain-rule-template.md @@ -0,0 +1,35 @@ +--- +domain: +last_rule_review_commit: +last_rule_updated_at: +--- + +# + +## 목적 / 책임 + +<이 도메인이 담당하는 책임을 1~2문장으로> + +## 포함 경로 + +- `/` — <이 경로가 이 도메인에 속하는 이유> + +## 제외 경로 + +- `/` — <왜 이 도메인이 아닌지> + +## 주요 구성 요소 + +- `` — <역할> + +## 유지할 패턴 + +- <네이밍 규칙 또는 아키텍처 패턴> + +## 다른 도메인과의 경계 + +- **<인접 domain>**: <어디까지가 이 도메인이고 어디서부터 저 도메인인지> + +## 금지 사항 + +- <이 도메인 코드에서 하면 안 되는 것> diff --git a/agent-ops/rules/common/_templates/test-case-rule-template.md b/agent-ops/rules/common/_templates/test-case-rule-template.md new file mode 100644 index 0000000..536adba --- /dev/null +++ b/agent-ops/rules/common/_templates/test-case-rule-template.md @@ -0,0 +1,79 @@ +--- +test_env: +test_profile: +domain: +verification_type: +last_rule_updated_at: +--- + +# 테스트 + +## 읽기 조건 + +- <이 도메인/검증 시나리오의 테스트, 검증, 실행 조건 판단이 필요한 경우> + +## 적용 범위 + +- <검증 대상 entrypoint, command, API, UX, profile, service> + +## 분류 + +- domain: +- verification_type: +- scope: <검증 대상 변경 범위 요약> + +## 환경 + +- host: +- port: +- runtime: +- package manager: +- docker: +- external service: +- model endpoint: +- credential: + +## 명령 + +- setup: +- lint: +- unit: +- smoke: +- e2e: +- model: +- full-cycle: + +## 필수 검증 + +- <작업 완료 전 반드시 실행하거나 판단해야 하는 검증> + +## 보조 검증 + +- <실행하면 좋지만 필수 검증을 대체하지 않는 smoke/helper 검증> + +## 판정 기준 + +- <통과로 판단할 출력, 상태, UX, artifact, endpoint 조건> + +## 기준 출력 예시 + +```text +<필요한 경우 기대 출력 예시> +``` + +## 차단 기준 + +- <테스트를 계속할 수 없는 환경/권한/도구/서비스 조건> + +## 보고 항목 + +- 실행한 명령: +- 성공한 검증: +- 실패/차단된 검증: +- 생략 사유: +- 남은 위험: + +## 금지 사항 + +- <이 도메인/검증 시나리오에서 하면 안 되는 것> +- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다. diff --git a/agent-ops/rules/common/_templates/test-env-rules-template.md b/agent-ops/rules/common/_templates/test-env-rules-template.md new file mode 100644 index 0000000..66cca94 --- /dev/null +++ b/agent-ops/rules/common/_templates/test-env-rules-template.md @@ -0,0 +1,38 @@ +--- +test_env: +last_rule_updated_at: +--- + +# 테스트 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +## 공통 규칙 + +- 테스트/검증은 이 파일을 기준으로 판단한다. +- 작업 완료 검증은 변경 범위 기준으로 선택한다. +- 필수 검증을 실행하지 못하면 차단 사유로 보고한다. +- 최종 보고에는 실행 명령, 결과, 생략 사유, 남은 위험을 남긴다. +- 환경값, secret, 개인 endpoint는 tracked docs/roadmap에 쓰지 않는다. + +## 기본 환경 + +- host: +- port: +- runtime: +- package manager: +- docker: +- external service: +- model endpoint: +- credential: + +## 라우팅 + +- / / : `agent-test//.md` + +## 라우팅 규칙 + +- 여러 항목이 맞으면 모두 읽는다. +- 도메인 매핑이나 domain rule이 있으면 각 도메인의 `-smoke` 문서를 기본 baseline으로 둔다. +- 도메인이 아직 없을 때만 `project-smoke`를 fallback baseline으로 둔다. +- 도메인/검증 시나리오별 문서는 다른 테스트 문서로 라우팅하지 않는다. diff --git a/agent-ops/rules/common/philosophy.md b/agent-ops/rules/common/philosophy.md new file mode 100644 index 0000000..b1b41f7 --- /dev/null +++ b/agent-ops/rules/common/philosophy.md @@ -0,0 +1,98 @@ +# Agent-Ops 철학 + +이 문서는 agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 읽는다. +일반 구현 작업에서는 읽지 않는다. + +## 핵심 + +- agent-ops는 AI agent가 작업하기 위한 규칙이자 가이드다. +- 사람 문서처럼 장황하게 설명하지 않고, agent가 바로 실행할 수 있게 작성한다. +- 필요한 컨텍스트만 읽게 만든다. 모든 문서를 항상 읽게 만들지 않는다. +- 애매한 형식을 만들지 않는다. 경로, 상태, id, 입력, 출력은 판별 가능해야 한다. +- 문서는 짧고 단단해야 한다. 길어서 이해되는 문서보다 짧아서 헷갈리지 않는 문서가 낫다. + +## 문서 작성 + +- 규칙과 스킬은 핵심만 쓴다. +- 같은 말을 여러 문서에 반복하지 않는다. 한 곳에 두고 링크한다. +- 설명보다 조건, 입력, 행동, 금지 사항을 우선한다. +- "적절히", "필요하면", "가능하면" 같은 말은 판별 기준이 없으면 쓰지 않는다. +- 예외가 있으면 예외 조건을 같이 쓴다. +- 긴 배경 설명은 README나 별도 참조 문서로 보내고, 실행 문서에는 실행 규칙만 남긴다. +- 룰 문서는 협업자가 직접 읽는 계약 문서이므로 한국어 `한다`체로 작성한다. +- README, GUIDE, roadmap 문서는 사람이 함께 검토하는 협업 문서이므로 한국어 설명체 또는 존댓말을 사용할 수 있다. +- 스킬 문서는 실행 안정성을 우선한다. 한국어 또는 영어를 사용할 수 있고, 이미 잘 동작하는 절차 계약은 언어 통일만을 위해 수정하지 않는다. +- path, filename, 상태값, id, regex, command, frontmatter key, runtime protocol token은 원문 ASCII 식별자를 유지한다. + +## 라우팅 + +- 라우팅은 얕아야 한다. +- 1홉은 진입 파일에서 공통/프로젝트 규칙을 읽는 단계다. +- 2홉은 규칙에서 domain rule, roadmap rule, router를 따라가는 단계다. +- 3홉은 router에서 SKILL.md를 읽는 단계다. +- 4홉은 skill이 템플릿이나 참조 문서를 추가로 읽는 단계다. +- 4홉 이상이 필요하면 구조가 과하게 쪼개졌는지 먼저 의심한다. 필요하면 앞 문서에 바로 가는 링크를 추가한다. +- 깊은 링크 체인을 만들지 않고, 필요한 문서가 무엇인지 앞 문서에서 바로 보이게 한다. +- 일반 작업마다 router, 모든 skill, 전체 roadmap, archive를 읽게 만들지 않는다. + +## LLM과 런타임 + +- LLM은 의미 판단, 범위 판단, 요약, 설계 선택을 맡는다. +- 런타임은 파일명, 폴더명, 상태값, exit code처럼 결정적으로 판별 가능한 일을 맡는다. +- LLM 없이 처리할 수 있는 구간은 파일 규약으로 뺀다. +- 런타임 신호는 문서 본문보다 경로와 이름에 둔다. +- 런타임 신호를 만들 때는 agent가 본문을 읽지 않아도 판별 가능해야 한다. +- `m-` 같은 prefix는 런타임 판별을 위한 신호다. +- code-review는 PASS 산출물을 만들고 완료 이벤트 메타데이터를 남긴다. +- roadmap 반영 여부와 호출 타이밍은 런타임이 완료 이벤트를 보고 판단한다. + +## 스킬 그룹 경계 + +- 스킬 그룹은 자기 라이프사이클 안에서만 강한 계약을 가진다. +- 스킬 그룹끼리는 다른 그룹의 내부 절차를 자동 호출하거나 전제하지 않는다. +- 그룹 간 연결은 사용자 명령, 런타임 이벤트, 또는 판별 가능한 파일 상태로만 한다. +- 구현 작업 그룹은 `plan`과 `code-review`의 PLAN/CODE_REVIEW 루프를 소유한다. +- 로드맵 그룹은 `create-roadmap`과 `update-roadmap`의 문서 작성, 의미 판단, 배치 제안, file-based fallback 갱신 흐름을 소유한다. +- Phase/Milestone 상태 전환, 구현 잠금 동기화, archive 이동, 완료 이벤트 반영처럼 결정적으로 판별 가능한 action은 NomadCode Core 또는 런타임이 있으면 그쪽 책임으로 둔다. +- `code-review`가 남긴 PASS 완료 이벤트를 로드맵에 반영할지, 언제 반영할지는 런타임이 판단하고 Core/MCP action이 있으면 그쪽으로, 없으면 `update-roadmap` file-based fallback 흐름으로 넘긴다. +- cross-project 잠금이나 `.agent-roadmap-sync` 같은 workspace-level 상태는 로드맵 그룹, Core, 런타임의 공동 경계다. `plan`과 `code-review`에 의존성 동기화 규칙을 넣지 않는다. +- 새 스킬을 만들기 전에 기존 그룹의 자연스러운 입력, 상태 전환, 후처리 지점에 넣을 수 있는지 먼저 본다. 다만 다른 그룹 경계를 침범해야 하면 런타임 이벤트로 분리한다. + +## 로드맵 + +- roadmap은 장기 기억이고, agent-task는 실행 상태다. +- current는 현재 작업 하나가 아니라 브랜치별 로컬 활성 Phase/Milestone 후보 창이다. +- Phase와 Milestone은 큰 방향과 기능 단위를 담는다. +- 구현 계획은 agent-task의 PLAN/CODE_REVIEW 루프에 둔다. +- 완료 후보는 바로 archive하지 말고 `[검토중]`으로 둔다. +- archive는 일반 작업에서 읽지 않는다. 과거 근거가 필요할 때만 링크를 따라 읽는다. + +## 스킬 + +- skill은 절차 문서다. +- skill 하나에 책임 하나만 둔다. +- skill이 다른 skill을 자동으로 깊게 호출하는 구조를 만들지 않는다. +- skill 본문은 실행에 필요한 규칙만 둔다. +- 템플릿은 출력 형식이 흔들릴 때만 둔다. +- 스킬 업데이트 시 router, rules, template, 출력 형식이 같은 계약을 말하는지 같이 확인한다. +- plan과 code-review처럼 짝 계약을 양쪽에서 반복해 강제하는 구조는 의도된 중복으로 본다. 동작 중인 짝 계약은 일관성 정리만을 위해 합치지 않는다. + +## 좋은 구조 + +- 진입 파일은 최소 규칙만 둔다. +- common rules는 공통 시작점만 둔다. +- project rules는 프로젝트 특화 판단만 둔다. +- domain rules는 특정 코드 영역 규칙만 둔다. +- skills는 반복 작업 절차만 둔다. +- roadmap은 장기 목표와 기능 단위만 둔다. +- agent-task는 실행 중인 작업 상태와 완료 산출물만 둔다. + +## 경고 신호 + +- 같은 내용을 세 군데 이상 설명하고 있다. +- 어떤 문서를 읽어야 할지 문서 안에서 다시 찾아야 한다. +- 상태값이 사람은 이해하지만 런타임은 판별하기 어렵다. +- skill이 너무 많은 예외를 품고 있다. +- README가 내부 규칙 문서처럼 길어지고 있다. +- archive를 일반 작업 컨텍스트로 끌어오고 있다. +- LLM이 파일명만 봐도 될 일을 본문까지 읽어 판단하고 있다. diff --git a/agent-ops/rules/common/rules-roadmap.md b/agent-ops/rules/common/rules-roadmap.md new file mode 100644 index 0000000..0e0da16 --- /dev/null +++ b/agent-ops/rules/common/rules-roadmap.md @@ -0,0 +1,158 @@ +# 로드맵 규칙 + +`agent-roadmap/` 디렉터리가 있는 프로젝트에서만 적용한다. + +## 구조 + +- 최상위 로드맵은 `agent-roadmap/ROADMAP.md`다. +- 활성 Phase는 `agent-roadmap/phase//PHASE.md`에 둔다. +- 활성 Milestone은 해당 Phase 아래 `agent-roadmap/phase//milestones/.md`에 둔다. +- ``와 ``는 소문자 영문, 숫자, 하이픈만 사용한다. +- 완료된 Phase는 scaffold 그대로 `agent-roadmap/archive/phase//PHASE.md`로 이동하고, 하위 Milestone도 `archive/phase//milestones/` 아래에 둔다. +- 진행중 Phase 안에서 완료된 Milestone은 활성 `PHASE.md`에 짧은 archive 링크를 남기고, 상세 문서는 `agent-roadmap/archive/phase//milestones/`로 이동한다. +- archive `PHASE.md`는 Phase 자체가 완료/폐기될 때만 만들며, 진행중 Phase의 완료 Milestone만 archive된 경우 archive Phase 디렉터리에 `milestones/`만 있을 수 있다. + +## Runtime Action Boundary + +- 로드맵 스킬은 기본적으로 로드맵 문서 작성, 의미 판단, 배치 제안, file-based fallback 갱신을 담당한다. +- Core/MCP가 있는 프로젝트에서는 Phase/Milestone 상태 전환, archive 이동, 외부 의존 lock 동기화, 완료 이벤트 반영 같은 action을 Core/MCP 또는 런타임이 처리한다. +- Core/MCP가 없거나 아직 해당 action을 제공하지 않는 프로젝트에서만 `update-roadmap` 스킬이 file-based fallback으로 직접 문서를 갱신한다. +- `update-roadmap`은 fallback 갱신을 수행하더라도 런타임 action 경계를 문서화하고, Core/MCP로 넘길 수 있는 입력과 결과를 함께 남긴다. + +## 로딩 + +- 세션 최초 1회 `agent-roadmap/current.md`가 있으면 읽고 활성 Phase, 활성 Milestone의 이름, 경로, 선택 규칙만 짧게 기억한다. +- `agent-roadmap/current.md`는 브랜치별 로컬 포인터이며 git 추적 대상이 아니다. +- `current.md`가 없고 로드맵 기반 계획 또는 갱신이 필요하면 `agent-ops/skills/common/_templates/roadmap-current-template.md` 형식으로 로컬 파일을 만들거나 `ROADMAP.md`와 활성 `PHASE.md`에서 후보를 고른다. +- 읽기 전용 로드맵 현지점 확인에서는 `current.md`가 없어도 만들지 않고, 로컬 current 없음으로 보고한 뒤 `ROADMAP.md`의 Phase 흐름만 보여준다. +- 일반 작업에서는 `ROADMAP.md`를 읽지 않는다. +- 일반 작업에서는 `agent-roadmap/archive/**`를 읽지 않는다. +- 기능 추가, 구조 변경, 구현 계획 전에는 요청과 변경 파일에 맞는 활성 Phase와 활성 Milestone 문서를 읽는다. +- 로드맵 현지점 확인은 로컬 `current.md`, `ROADMAP.md`의 Phase 흐름, 활성 `PHASE.md`의 Milestone 흐름, 활성 Milestone의 제목/목표/상태만 기본으로 읽는다. +- `ROADMAP.md`는 로드맵 생성/갱신, Phase 추가/삭제/전환, 전체 구조 변경, 활성 범위 밖 작업 확인 때만 읽는다. +- 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `ROADMAP.md` 또는 `PHASE.md`에 있는 archive 링크를 따라가서 필요한 archive 문서만 읽는다. + +## Phase와 Milestone 선택 + +- `current.md`는 현재 작업 위치가 아니라 활성 Phase와 활성 Milestone 후보 목록이다. +- `current.md`는 공유 진행 상태가 아니며, 공유해야 할 상태는 `ROADMAP.md`, `PHASE.md`, Milestone 문서, `.agent-roadmap-sync/locks.yaml`에 남긴다. +- 활성 Phase는 `agent-roadmap/phase/**/PHASE.md`만 대상으로 한다. +- 활성 Milestone은 `agent-roadmap/phase/**/milestones/*.md`만 대상으로 한다. +- `current.md`에는 `[완료]` 또는 `[폐기]` Phase/Milestone을 남기지 않는다. 완료 후보는 승인 전까지 `[검토중]`으로 둔다. +- "로드맵에 추가", "마일스톤에 추가"처럼 target 없는 신규 작업 추가 요청은 `update-roadmap` 스킬로 배치 제안 또는 file-based fallback 갱신을 처리하고, Phase/Milestone/Epic/Task 배치를 자동 판단한다. +- target 없는 신규 추가 요청은 먼저 요청 규모를 `phase`, `milestone`, `epic`, `task`, `subtask`, `context` 중 가장 작은 충분한 단위로 판정한다. +- target 없는 신규 추가 요청은 활성 창만으로 결정하지 말고 필요한 경우 `ROADMAP.md`의 Phase 흐름과 관련 Phase/Milestone 문서를 비교한다. +- 배치는 Phase -> Milestone -> Epic -> Task 순서로 내려가며 같은 레벨의 동일/유사 후보를 먼저 찾는다. +- 동일/유사 항목이 이미 있으면 새로 만들지 말고 기존 항목을 업데이트한다. +- 적절한 기존 후보가 없을 때만 판정한 규모에 맞는 새 항목을 만든다. +- 부모 후보는 있고 판정 규모의 항목만 없으면 부모 아래에 새 항목을 만들고, 부모도 없을 때만 필요한 부모 항목을 함께 만든다. +- 자동 배치할 때는 선택한 Phase/Milestone/Epic/Task와 밀린 후보의 이유를 결과에 남긴다. +- `current.md`가 아카이브 경로를 가리키면 해당 항목은 활성 후보로 읽지 말고 로드맵 갱신이 필요하다고 보고한다. +- 선택한 Phase/Milestone의 목표 또는 범위 제외와 요청이 충돌하면 구현 전에 사용자에게 확인한다. + +## 상태 표기 + +- Phase와 Milestone 상태 표기는 `[스케치]`, `[계획]`, `[진행중]`, `[검토중]`, `[완료]`, `[보류]`, `[폐기]` 중 하나만 사용한다. +- `[스케치]`는 방향성, 문제의식, 후보 범위, 미정 질문을 기록하는 컨셉 상태다. 구현 가능한 계획이 아니므로 `agent-task` 구현 계획 생성과 코드 구현을 시작하지 않는다. +- `[스케치]` 항목은 `[계획]`으로 승격하기 위한 `승격 조건`, 사용자 결정, 범위 경계, 후속 Milestone 후보를 정리하는 것이 목적이다. +- `[계획]` 이상 상태의 Milestone에서 `승격 조건` 섹션은 선택 사항이다. 섹션이 없거나 `- 없음`이면 템플릿 오류로 보지 않는다. +- `[스케치]`를 `[계획]`으로 전환하려면 `승격 조건`의 미정 항목이 해소되고, 목표, 범위, 기능 Task, 직접적인 사용자 결정 항목, 후속 구현 단위가 구현 계획을 만들 수 있을 만큼 정리되어야 한다. +- `[계획]`은 목표, 범위, 기능 Task, 구현 잠금, 결정 필요 항목이 정리되어 구현 계획을 만들 수 있는 상태다. +- 갱신 범위에 포함된 기존 진행 상태 표기는 `[진행중]`으로 정리한다. +- `[검토중]`은 모든 기능 Task와 Task 안에 명시된 검증이 충족된 것으로 보이나, 사용자의 최종 완료 확인과 archive 승인이 아직 남은 완료 후보 상태다. +- `[검토중]` 항목은 활성 경로에 남기고 `current.md`의 활성 후보로 유지할 수 있다. +- 검토 결과 보완이 필요하면 별도 reopen 상태를 만들지 않고 `[진행중]`으로 되돌린 뒤 `완료 리뷰` 또는 `작업 컨텍스트`에 보완 방향을 남긴다. +- 검토 결과 보류 또는 폐기 결정이 나면 `[보류]` 또는 `[폐기]`로 전환한다. +- `ROADMAP.md`의 Phase 흐름과 `PHASE.md`의 Milestone 흐름은 완료, 검토중, 진행중, 계획, 스케치 순서를 기본으로 하며 아래로 갈수록 미래 작업에 가까워지게 정렬한다. + +## 구현 잠금 + +- `구현 잠금`은 승인 의식이 아니라 사용자 결정이 필요한지 표시하는 얇은 상태다. +- 제품 방향, 범위, 우선순위, 책임 경계처럼 사용자만 결정할 수 있는 항목이 남아 있으면 상태를 `잠금`으로 두고 `결정 필요` 체크리스트에 질문을 적는다. +- 기존 구조, 도메인 rule, 플랫폼 관례, 업계 표준으로 합리적으로 정할 수 있는 항목은 `결정 필요`가 아니라 `작업 컨텍스트`의 표준선이나 구현 가정으로 기록한다. +- Milestone 전체에서 사용자만 결정할 항목이 더 이상 없고 에이전트가 표준선에 따라 실행하면 되는 상태라면 `해제`로 둔다. +- 선택한 Milestone에 `구현 잠금` 섹션이 없거나 상태가 `잠금`이면 코드 구현, `agent-task` 구현 계획, 세부 API/파일 구조 확정을 시작하기 전에 현재 요청에 직접 영향을 주는 `결정 필요` 항목만 사용자에게 확인한다. +- 현재 요청과 직접 관련 없는 미정 항목은 잠금 상태로 남겨도 되며, 표준선으로 처리 가능한 작업을 막지 않는다. +- 잠금 상태를 바꾸더라도 `기능` Task를 자동 완료 처리하지 않는다. +- `[스케치]` 상태의 Milestone은 `구현 잠금`이 `해제`로 보이더라도 구현 계획과 코드 구현 대상이 아니다. 먼저 `[계획]`으로 승격해야 한다. + +## 프로젝트 간 잠금 + +- 프로젝트 상위 `.agent-roadmap-sync/locks.yaml`은 프로젝트 간 Milestone 잠금 인덱스다. 외부 의존 잠금을 생성하거나 동기화해야 하면 Core/MCP가 있으면 그쪽 action이 만들고, 없으면 `update-roadmap` file-based fallback이 디렉터리와 파일을 만든다. +- entry는 `id`, `locked`, `rely-on[].target`, `rely-on[].status`, `rely-on[].note`만 사용한다. +- `locks.yaml`은 root sequence block style을 기본으로 작성한다. 예: `- id: ...` 아래에 `locked`, `rely-on`을 둔다. +- `id`는 기본적으로 `<잠긴-project>:<잠긴-milestone-slug>`로 만든다. +- `locked`와 `rely-on[].target`은 `:agent-roadmap/phase//milestones/.md` 형식으로 기록한다. +- Milestone 경로가 `agent-roadmap/...` 상대 경로이면 현재 프로젝트명을 prefix로 붙인다. workspace 하위 절대/상대 경로이면 workspace 바로 아래 디렉터리명을 project로 삼고, 그 뒤 `agent-roadmap/...` 경로를 붙인다. +- `locked`는 잠긴 Milestone, `rely-on.target`은 선행 조건 Milestone이다. 둘 다 같은 workspace의 어느 활성 Phase 하위 Milestone이어도 된다. 의존 대상이 `current.md`에 있어야 한다고 가정하지 않는다. +- "현재 마일스톤은 X 프로젝트 작업 뒤에 진행", "X 프로젝트 때문에 현재 작업 잠금", "의존성 설정해"처럼 잠긴 Milestone을 생략한 외부 의존 잠금 요청은 현재 프로젝트 로컬 `current.md`의 활성 Milestone 단일 후보를 잠긴 대상으로 삼는다. +- 의존 대상은 명시 경로, 명시 slug, 명시 제목, 잠긴 Milestone 문서의 선행 Milestone 힌트, 대상 프로젝트 로컬 `current.md`가 있을 때의 단일 후보 순서로 확정한다. +- 정규화 비교는 소문자 변환, backtick/따옴표 제거, 영문/숫자가 아닌 연속 문자를 `-` 하나로 치환, 앞뒤 `-` 제거 후 Milestone 파일 slug와 정규화한 제목에 대조한다. +- 의존 대상 탐색은 대상 프로젝트의 `agent-roadmap/phase/*/milestones/*.md` 활성 문서만 대상으로 한다. archive 문서는 사용자가 archive 경로를 명시한 경우 외에는 읽거나 후보로 삼지 않는다. +- 후보가 없거나 둘 이상이면 locks.yaml을 만들거나 고치지 말고 사용자에게 대상 Milestone 선택을 요청한다. +- 외부 의존 잠금을 만들 때 대상 Milestone의 `구현 잠금`은 `잠금`으로 둔다. +- 새 `rely-on.status`는 선행 Milestone 상태에서 파생한다. `[검토중]` 또는 `[완료]`이면 `enable`, 그 외 상태거나 상태를 확인할 수 없으면 `disable`이다. +- 같은 `id` entry를 upsert할 때 기존 `rely-on` 항목을 삭제하지 않는다. 같은 `rely-on.target`만 status/note를 갱신하고, 없는 target은 추가하며, `locked` 경로가 바뀐 경우에만 `locked`를 갱신한다. +- `locks.yaml`이 있고 Milestone을 갱신하거나 archive할 때는 대상 Milestone identity로 `agent-ops/bin/roadmap-dependency-checker.sh --find-milestone "" both ""`를 먼저 실행한다. +- find 결과가 `none`이면 결과 보고의 `Workspace 잠금`에 `관련 lock 없음`을 남긴다. 외부 의존 잠금 생성/동기화 요청이 아니라면 `locks.yaml`을 새로 만들거나 수정하지 않는다. +- Core/MCP action 또는 `update-roadmap` fallback이 갱신한 Milestone identity가 어느 entry의 `rely-on.target`과 일치하면 Milestone 상태 기준으로 `status`를 동기화한다. `[검토중]` 또는 `[완료]`이면 `enable`, 그 외 상태면 `disable`이다. +- Core/MCP action 또는 `update-roadmap` fallback이 갱신하거나 선택한 Milestone identity가 어느 entry의 `locked`와 일치하면 모든 `rely-on.status`가 `enable`인지 결과 보고에 남긴다. 모든 조건이 충족되어도 잠금 해제 실행은 Core/MCP 또는 런타임의 별도 action으로 처리한다. +- archive 모드에서는 파일 이동 전에 대상 Milestone의 활성 경로 identity를 보존하고, 그 identity로 `--find-milestone "" both ""`를 먼저 실행한다. 보존한 identity가 어느 entry의 `rely-on.target`과 일치하고 Milestone 상태가 `[완료]`이면 archive 이동 전에 해당 `rely-on.status`를 `enable`로 바꾼다. +- archive 모드에서 보존한 identity가 어느 entry의 `locked`와 일치하면 archive 이동 전에 모든 `rely-on.status`가 `enable`인지 결과 보고에 남긴다. 미충족이어도 archive 자체는 막지 않고 `Workspace 잠금: 미충족`으로 보고한다. +- 잠금 해제 조건 충족 여부만 확인할 때는 `agent-ops/skills/common/check-roadmap-dependency/SKILL.md`를 읽는다. lock id가 없으면 해당 스킬은 `agent-ops/bin/roadmap-dependency-checker.sh --find-milestone "" "" ""`로 현재 Milestone이 `locked`인지 `rely-on.target`인지 양방향으로 찾은 뒤 `agent-ops/bin/roadmap-dependency-checker.sh "" ""`를 사용한다. +- checker exit code는 `0=true`, `1=false`, `2=설정/입력/파싱 오류`로 해석한다. + +## Epic과 Task id + +- Milestone 문서의 실행 체크리스트는 `기능` 섹션 하나로 작성한다. 새 Milestone이나 갱신 범위에 포함된 Milestone에는 별도 `완료 기준` 섹션을 만들지 않는다. +- 기존 Milestone에 `필수 기능`과 `완료 기준`이 분리되어 있으면, 갱신 시 `완료 기준`을 관련 기능 Task 안의 선택적 `검증:` 문구로 흡수하고 섹션을 제거한다. +- 기능 Task는 기능 또는 산출물 단위다. 검증이 필요한 기능만 같은 Task 안에 `검증: <명령/확인 방법/기대 결과>`를 붙인다. +- 검증이 명시된 Task의 `[x]`는 기능/산출물과 해당 검증이 모두 충족되었다는 뜻이다. 검증이 명시되지 않은 Task의 `[x]`는 기능/산출물 완료 근거가 충분하다는 뜻이다. +- 사용자만 결정할 수 있는 검토/선택/우선순위 항목은 기능 Task로 쓰지 않는다. `구현 잠금`의 `결정 필요` 또는 `작업 컨텍스트`로 분리하고, 현재 구현에 직접 필요하면 plan 생성 전에 사용자 확인을 받는다. +- `기능` 섹션의 Task 체크리스트는 Epic 바로 아래의 flat list를 기본으로 한다. 구현 세부, 테스트만 따로 떼어낸 하위 체크박스는 roadmap에 만들지 말고 plan 내부 체크리스트나 같은 Task의 `검증:`으로 흡수한다. +- Epic heading은 `### Epic: [epic-id] <이름>` 형식을 사용한다. +- Task는 `- [ ] [item-id] 설명` 또는 `- [x] [item-id] 설명` 형식을 사용한다. +- epic-id와 item-id는 공백 없는 짧은 ASCII 토큰이며, 영문/숫자 segment 1~4개로 작성하고 segment 구분자는 `-`, `_`, `+`, `=`만 사용한다. 가능하면 1~3 segment를 우선하며, 전체 길이는 32자 이하를 권장한다. +- epic-id와 item-id는 해당 Milestone 안에서만 유일하면 된다. +- 다른 Milestone에서는 같은 id를 다시 사용할 수 있다. 여러 Milestone 후보에서 같은 id가 발견되면 Milestone 이름이나 문서 경로로 대상을 확정한다. +- 사용자가 epic-id 또는 item-id를 언급하면 해당 Milestone의 Epic/Task 항목을 우선 anchor로 삼고, 기존 id는 명시적 요청 없이 바꾸지 않는다. + +## Milestone 기반 agent-task + +- `plan` 스킬이 활성 Milestone 범위의 구현 계획을 만들면 task group은 `agent-task/m-/` 형식을 사용한다. +- ``는 활성 Milestone 파일명에서 `.md`를 제거한 값이며, Phase slug, Epic id, Task id, 별도 task slug를 task group에 넣지 않는다. +- split 작업은 기존 규칙 그대로 `agent-task/m-//` 아래에 둔다. +- `m-`는 Milestone 기반 작업 전용 예약 prefix이며, 일반 작업 task group은 `m-`으로 시작하지 않는다. +- 런타임은 파일 내부가 아니라 task group 이름만으로 Milestone 기반 작업 여부를 판별한다. +- `code-review`에서 `m-` 작업이 PASS되면 roadmap을 직접 수정하거나 `update-roadmap`을 직접 호출하지 않는다. +- 런타임은 PASS 완료 이벤트의 task group에서 `m-`를 판별하고, 상태 체크 후 Core/MCP action으로 Milestone 업데이트를 호출한다. Core/MCP action이 없으면 `update-roadmap` file-based fallback 흐름을 호출한다. 단, Milestone 기능 Task 체크는 `complete.log`에 `Roadmap Completion` 섹션과 명시 Task id가 있을 때만 수행하고, 섹션이 없으면 no-op으로 둔다. +- 런타임 완료 이벤트가 최종 archive 경로만 갖고 있으면 `agent-task/archive/YYYY/MM/m-/...`를 `agent-task/m-/...` 형태의 `origin-task`로 정규화해 전달한다. +- 런타임 호출에서 매칭되는 활성 Milestone이 없거나 둘 이상이면 추정하지 말고 수동 target 선택이 필요하다고 보고한다. +- `WARN` 또는 `FAIL`은 Milestone 완료 업데이트를 하지 않는다. 일반적으로 같은 `m-` task group에서 follow-up plan/review를 이어가지만, code-review의 user-review gate가 트리거되면 `USER_REVIEW.md`를 남기고 사용자 판단을 기다린다. +- `[스케치]` Milestone은 Milestone 기반 `agent-task` 생성 대상이 아니다. 런타임이나 plan 스킬은 이를 구현 작업으로 라우팅하지 않고 `[계획]` 승격 필요를 보고한다. + +## 완료 리뷰 + +- Task 완료나 Milestone 갱신 시 모든 기능 Task와 Task 안에 명시된 검증이 evidence와 함께 `[x]`가 되었는지 확인한다. +- 모두 충족된 것으로 보이면 Milestone을 `[완료]`로 바로 바꾸거나 archive로 이동하지 말고 `[검토중]`으로 바꾼다. +- `[검토중]`으로 바꿀 때는 Milestone 문서에 `완료 리뷰` 섹션을 만들거나 갱신하고, 완료 근거 1~3줄과 사용자에게 필요한 최종 확인 항목을 남긴다. +- 사용자가 완료를 승인한 뒤에만 `[완료]`로 전환하고 archive 이동을 수행한다. +- Phase도 모든 하위 Milestone이 `[완료]` 또는 `[폐기]`로 정리되어 Phase 완료 후보가 되면 `[검토중]`으로 두고 사용자 최종 확인을 받은 뒤 `[완료]` 또는 `[폐기]`로 전환한다. + +## 로드맵 현지점 + +- 현재 작업 지점이나 로드맵상 현 위치 확인 요청은 `analyze-roadmap-position` 스킬로 처리한다. +- 답변은 `agent-ops/skills/common/_templates/roadmap-position-report-template.md` 섹션과 필드 순서를 따른다. +- 기본 동작에서는 코드, git 상태, diff를 읽지 않고 `로드맵 > Phase > Milestone` breadcrumb와 흐름 목록으로 현재 좌표를 보여준다. +- current가 Phase 또는 Milestone 후보를 여럿 가리키면 모두 `현재 후보`로 표시하고 짧은 역할 태그만 붙인다. + +## 아카이브 + +- 완료 또는 폐기되어 현재 작업 후보에서 제외할 Phase/Milestone은 Core/MCP action으로 아카이빙한다. Core/MCP action이 없으면 `update-roadmap` file-based fallback으로 아카이빙한다. +- `[검토중]` Phase/Milestone은 archive 대상이 아니며, 사용자 승인 전까지 활성 경로에 남긴다. +- Phase 아카이브 대상은 `agent-roadmap/archive/phase//PHASE.md`와 같은 scaffold로 이동한다. +- Milestone 아카이브 대상은 `agent-roadmap/archive/phase//milestones/.md`로 이동한다. +- Milestone 아카이브 전에는 이동 전 활성 경로 identity로 `.agent-roadmap-sync/locks.yaml`을 확인한다. 해당 identity가 `rely-on.target`이면 `[완료]` 상태에서 `enable`로 동기화하고, 해당 identity가 `locked`이면 의존 조건 충족 여부를 보고하며, 어느 쪽에도 없으면 `관련 lock 없음`으로 보고한다. +- 아카이빙할 때는 활성 `ROADMAP.md` 또는 활성 `PHASE.md`에 archive 문서 링크와 짧은 요약만 남긴다. +- 아카이브된 Phase/Milestone은 로컬 `current.md`에 남기지 않고, 일반 Phase/Milestone 선택이나 위치 분석의 후보로 삼지 않는다. +- 아카이브 문서는 과거 기록 스냅샷으로 보고, 최신 템플릿이나 스킬 규약에 맞춰 재포맷하지 않는다. diff --git a/agent-ops/rules/common/rules.md b/agent-ops/rules/common/rules.md new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/agent-ops/rules/common/rules.md @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/agent-ops/skills/common/_templates/implementation-user-review-request-section.md b/agent-ops/skills/common/_templates/implementation-user-review-request-section.md new file mode 100644 index 0000000..bce0022 --- /dev/null +++ b/agent-ops/skills/common/_templates/implementation-user-review-request-section.md @@ -0,0 +1,11 @@ +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 diff --git a/agent-ops/skills/common/_templates/roadmap-current-template.md b/agent-ops/skills/common/_templates/roadmap-current-template.md new file mode 100644 index 0000000..3e4b94b --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-current-template.md @@ -0,0 +1,29 @@ +# 현재 로드맵 컨텍스트 + +## 활성 Phase + +- [<스케치 | 계획 | 진행중 | 검토중 | 보류>] + - 경로: `agent-roadmap/phase//PHASE.md` + +## 활성 Milestone + +- [<스케치 | 계획 | 진행중 | 검토중 | 보류>] + - Phase: `agent-roadmap/phase//PHASE.md` + - 경로: `agent-roadmap/phase//milestones/.md` + +## 선택 규칙 + +- 이 문서는 활성 Phase와 활성 Milestone 후보 목록이며, 개인별 현재 작업 위치나 완료 상태를 기록하지 않는다. +- 이 문서는 브랜치별 로컬 포인터이며 git 추적 대상이 아니다. +- 활성 Phase는 `agent-roadmap/phase//PHASE.md`를 가리킨다. +- 활성 Milestone은 `agent-roadmap/phase//milestones/.md`를 가리킨다. +- 활성 항목은 아카이브 경로를 포함하지 않는다. +- `[검토중]` 항목은 사용자 완료 확인 전까지 활성 항목으로 남길 수 있다. +- `[스케치]` 항목은 활성 후보로 남길 수 있지만 구현 계획 생성 대상은 아니며, `[계획]` 승격 조건을 정리하는 대상으로만 다룬다. +- `[완료]` 또는 `[폐기]` 항목은 archive 링크를 남긴 뒤 활성 항목에서 제거한다. +- 요청 내용, 현재 브랜치, 변경 파일, 관련 코드 경로를 보고 가장 관련 있는 Phase와 Milestone을 선택하고 같은 세션에서 1회 읽는다. +- 활성 Phase 또는 Milestone 둘 이상에 걸치면 필요한 문서를 모두 읽고 작업 범위를 좁힌다. +- 활성 범위 밖의 작업이면 `agent-roadmap/ROADMAP.md`의 Phase 흐름을 확인하고 사용자에게 진행 또는 전환 여부를 확인한다. +- 완료된 과거 내용이 필요할 때만 `ROADMAP.md` 또는 `PHASE.md`에 있는 archive 링크를 따라가서 읽는다. +- 선택된 Milestone의 `구현 잠금` 섹션이 없거나 상태가 `잠금`이면 구현이나 구현 계획을 시작하기 전에 현재 요청에 직접 영향을 주는 `결정 필요` 항목만 확인한다. 관련 결정이 없고 표준선으로 처리 가능하면 잠금을 유지한 채 진행할 수 있으며, Milestone 전체에서 사용자만 결정할 항목이 더 이상 없을 때만 `구현 잠금` 상태를 `해제`로 둔다. +- 선택된 Milestone 상태가 `[스케치]`이면 `구현 잠금` 상태와 관계없이 구현이나 구현 계획을 시작하지 않고 `[계획]` 승격 필요를 보고한다. diff --git a/agent-ops/skills/common/_templates/roadmap-milestone-template.md b/agent-ops/skills/common/_templates/roadmap-milestone-template.md new file mode 100644 index 0000000..669bc84 --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-milestone-template.md @@ -0,0 +1,85 @@ +# Milestone: + +## 위치 + +- Roadmap: `agent-roadmap/ROADMAP.md` +- Phase: `agent-roadmap/phase//PHASE.md` + +## 목표 + +<이 Milestone이 끝났을 때 달성되어야 하는 결과를 1~3문장으로 작성> + +## 상태 + +[<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + +## 승격 조건 + + + +- 없음 + + + +## 구현 잠금 + +- 상태: <잠금 | 해제> +- 결정 필요: <없음 | 아래 체크리스트> + - [ ] <사용자만 결정할 수 있는 제품/범위/우선순위/책임 경계 질문> + +## 범위 + +- <이 Milestone에 포함되는 제품/기술/문서 범위> + +## 기능 + + + +### Epic: [epic-id] + +<이 Epic이 묶는 capability 또는 산출물 설명> + +- [ ] [item-id] <구현 세부가 아니라 이 Milestone에서 달성해야 할 capability 또는 산출물> + + + +## 완료 리뷰 + +- 상태: <없음 | 요청됨 | 승인됨 | 보완 필요 | 보류 | 폐기> +- 요청일: +- 완료 근거: <모든 기능 Task와 Task 안에 명시된 검증 충족 여부를 1~3줄로 요약> +- 리뷰 필요: + - [ ] 사용자가 완료 결과를 확인했다 + - [ ] archive 이동을 승인했다 +- 리뷰 코멘트: <없음 | 보완/보류/폐기 방향성> + +## 범위 제외 + +- <이 Milestone에서 의도적으로 하지 않는 일> + +## 작업 컨텍스트 + +- 관련 경로: `` +- 표준선(선택): <기존 구조, 도메인 rule, 플랫폼 관례, 업계 표준으로 진행할 기본 기준> +- 선행 작업: <없음 또는 Milestone/태스크 이름> +- 후속 작업: <없음 또는 Milestone/태스크 이름> +- 확인 필요: diff --git a/agent-ops/skills/common/_templates/roadmap-phase-template.md b/agent-ops/skills/common/_templates/roadmap-phase-template.md new file mode 100644 index 0000000..08daa5d --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-phase-template.md @@ -0,0 +1,23 @@ +# Phase: + +## 상태 + +[<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + +## 목표 + +<이 Phase가 끝났을 때 달성되어야 하는 결과와 책임 경계를 1~3문장으로 작성> + +## Milestone 흐름 + +완료된 Milestone은 archive 경로를 가리키고, 검토중, 진행중, 계획, 스케치 또는 보류 Milestone은 이 Phase 하위 `milestones/` 경로를 가리킨다. +완료, 검토중, 진행중, 계획, 스케치 순서로 두어 아래로 갈수록 미래 작업에 가까워지게 정렬한다. +스케치 Milestone은 아직 구현 가능한 계획이 아니므로 계획 Milestone보다 아래에 둔다. + +- [<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + - 경로: `agent-roadmap/phase//milestones/.md` 또는 `agent-roadmap/archive/phase//milestones/.md` + - 요약: <목표 또는 결과 1문장> + +## Phase 경계 + +- <이 Phase에서 유지할 책임 경계 또는 범위 제외 기준> diff --git a/agent-ops/skills/common/_templates/roadmap-position-report-template.md b/agent-ops/skills/common/_templates/roadmap-position-report-template.md new file mode 100644 index 0000000..ea057ea --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-position-report-template.md @@ -0,0 +1,21 @@ +# 로드맵 현지점 + +- [local current.md](agent-roadmap/current.md) + +- [로드맵: ](agent-roadmap/ROADMAP.md) + - [Phase: ](agent-roadmap/phase//PHASE.md) + - [Milestone: ](agent-roadmap/phase//milestones/.md) + - [<상태>] <목표 또는 역할 한 줄> + - [Milestone: ](agent-roadmap/phase//milestones/.md) + - [<상태>] <목표 또는 역할 한 줄> + +## 전체 Phase 흐름 + +- [<상태>] []() +- [<상태>] [<현재 Phase 이름>]() ← 현재 +- [<상태>] []() + +## 현재 Phase의 Milestone 흐름 + +- [<상태>] []() ← 현재 후보, <역할 태그> +- [<상태>] []() ← 현재 후보, <역할 태그> diff --git a/agent-ops/skills/common/_templates/roadmap-template.md b/agent-ops/skills/common/_templates/roadmap-template.md new file mode 100644 index 0000000..b42dd53 --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-template.md @@ -0,0 +1,42 @@ +# 로드맵 + +## 전체 목표 + +<프로젝트가 최종적으로 달성하려는 결과를 1~3문장으로 작성> + +## Phase 흐름 + +위에서 아래로 진행된 순서와 예정 흐름을 나타낸다. +완료된 Phase도 로드맵에서 제거하지 않고, archive의 Phase 문서로 연결한다. +검토중 또는 진행중 Phase는 계획 Phase보다 위에 두어, 아래로 갈수록 미래 작업에 가까워지게 정렬한다. +스케치 Phase는 아직 구현 가능한 계획이 아니므로 계획 Phase보다 아래에 둔다. + +- [<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + - 경로: `agent-roadmap/phase//PHASE.md` 또는 `agent-roadmap/archive/phase//PHASE.md` + - 요약: <이 Phase의 목표와 역할 1문장> + +## 로딩 정책 + +- 일반 작업에서는 `agent-roadmap/ROADMAP.md`를 매번 읽지 않는다. +- 기능 추가, 구조 변경, 스킬 추가/수정, 문서 구조 변경 작업을 수행할 때는 `agent-roadmap/current.md`를 먼저 읽는다. +- `current.md`는 현재 작업 위치가 아니라 활성 Phase와 활성 Milestone 후보 목록이다. +- `current.md`는 브랜치별 로컬 포인터이며 git 추적 대상이 아니다. 로드맵 기반 계획/갱신에서 없으면 `agent-ops/skills/common/_templates/roadmap-current-template.md` 형식으로 생성해 사용하고, 읽기 전용 현지점 확인에서는 생성하지 않는다. +- `current.md`에는 개인별 현재 작업 위치나 완료 상태를 기록하지 않는다. +- `current.md`의 활성 Phase는 `agent-roadmap/phase//PHASE.md`를 가리킨다. +- `current.md`의 활성 Milestone은 `agent-roadmap/phase//milestones/.md`를 가리킨다. +- `current.md`는 `agent-roadmap/archive/**` 경로를 활성 항목으로 포함하지 않는다. +- 요청 내용, 현재 브랜치, 변경 파일, 관련 코드 경로를 보고 가장 관련 있는 활성 Phase와 Milestone 문서를 같은 세션에서 1회 읽는다. +- 활성 Phase 또는 Milestone 밖의 작업이면 이 문서의 Phase 흐름을 확인하고 사용자에게 진행 또는 전환 여부를 확인한다. +- 이 문서는 로드맵 생성/갱신, Phase 전환, Phase 추가/수정, 전체 구조 변경 요청이 있을 때만 읽는다. +- 상세 작업은 각 Milestone 문서의 `기능`으로 관리한다. 검증이 필요한 기능만 같은 Task 안에 `검증:`으로 통합한다. +- `[스케치]` Phase/Milestone은 방향성, 문제의식, 후보 범위, 미정 질문을 기록하는 컨셉 상태이며 구현 계획 생성 대상이 아니다. +- `[스케치]` 항목은 `승격 조건`을 정리해 `[계획]`으로 전환한 뒤 구현 계획을 만든다. +- 모든 기능 Task와 Task 안에 명시된 검증이 충족된 Milestone은 먼저 `[검토중]`으로 두고, 사용자 완료 확인과 archive 승인을 받은 뒤 `[완료]`로 전환한다. +- 완료된 Phase는 `agent-roadmap/archive/phase//PHASE.md`로 이동하고, 하위 Milestone도 같은 archive Phase scaffold 아래에 둔다. +- 진행중 Phase 안에서 완료된 Milestone은 활성 Phase 문서에 짧은 링크를 남기고, 상세 문서는 `agent-roadmap/archive/phase//milestones/`로 이동한다. +- archive `PHASE.md`는 Phase 자체가 완료 또는 폐기될 때만 만들며, 진행중 Phase의 완료 Milestone만 archive된 경우 archive Phase 디렉터리에 `milestones/`만 있을 수 있다. +- `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `ROADMAP.md` 또는 `PHASE.md`의 archive 링크를 따라가서 읽는다. +- 아카이브된 Phase/Milestone 문서는 최신 템플릿이나 스킬 규약에 맞춰 재포맷하지 않는다. +- 선택된 Milestone의 `구현 잠금` 섹션이 없거나 상태가 `잠금`이면 코드 구현, `agent-task` 구현 계획 생성, 세부 API/파일 구조 확정을 시작하기 전에 현재 요청에 직접 영향을 주는 `결정 필요` 항목만 확인한다. +- 현재 요청과 직접 관련 없는 미정 항목은 잠금 상태로 남겨도 되며, 기존 구조/도메인 rule/플랫폼 관례로 정할 수 있는 작업은 표준선으로 기록하고 진행할 수 있다. +- Milestone 전체에서 사용자만 결정할 항목이 더 이상 없고 에이전트가 표준선에 따라 실행하면 되는 상태라면 `구현 잠금` 상태를 `해제`로 둔다. diff --git a/agent-ops/skills/common/_templates/skill-template.md b/agent-ops/skills/common/_templates/skill-template.md new file mode 100644 index 0000000..d3acdc2 --- /dev/null +++ b/agent-ops/skills/common/_templates/skill-template.md @@ -0,0 +1,56 @@ +--- +name: +version: 1.0.0 +description: <이 skill이 하는 일을 한 줄로 설명. 트리거 키워드 포함 권장> +--- + +# + +## 목적 + +<이 skill이 해결하는 문제를 1~2문장으로 설명> + +## 언제 호출할지 + +- <이 skill을 호출해야 하는 상황 1> +- <이 skill을 호출해야 하는 상황 2> +- <이 skill을 호출해야 하는 상황 3> + +## 입력 + +- ``: <설명> (필수) +- ``: <설명> (선택) + +## 먼저 확인할 것 + +- [ ] <실행 전 반드시 확인해야 할 조건 1> +- [ ] <실행 전 반드시 확인해야 할 조건 2> + +## 실행 절차 + +1. **<단계명>** + - <세부 행동> + - <세부 행동> + +2. **<단계명>** + - <세부 행동> + +3. **결과 보고** + - <출력할 내용> + +## 실행 결과 검증 + +- [ ] <실행 후 확인해야 할 성공 조건 1> +- [ ] <실행 후 확인해야 할 성공 조건 2> +- 검증 실패 시: <실패 시 취할 행동 — 롤백, 사용자 알림, 재시도 등> + +## 출력 형식 + +``` +<출력 예시> +``` + +## 금지 사항 + +- <절대 하면 안 되는 것> +- <절대 하면 안 되는 것> diff --git a/agent-ops/skills/common/analyze-roadmap-position/SKILL.md b/agent-ops/skills/common/analyze-roadmap-position/SKILL.md new file mode 100644 index 0000000..a8990e6 --- /dev/null +++ b/agent-ops/skills/common/analyze-roadmap-position/SKILL.md @@ -0,0 +1,74 @@ +--- +name: analyze-roadmap-position +version: 1.11.0 +description: "여러 레포를 전환할 때 코드/git 분석 없이 전체 ROADMAP > Phase > Milestone > current 현지점을 링크 달린 breadcrumb와 흐름 목록으로 빠르게 보여주는 읽기 전용 스킬" +--- + +# 로드맵 현지점 + +## 목적 + +여러 레포를 병렬 운용하다가 돌아왔을 때, 현재 작업이 전체 로드맵의 어느 Phase와 Milestone에 있는지 빠르게 보여준다. +기본 출력은 `로드맵 > Phase > Milestone` breadcrumb와 전체 Phase 흐름, 현재 Phase의 Milestone 흐름이다. +코드 진행도 감사, git diff 분석, 테스트 근거 확인, 남은 작업 정밀 판정은 기본 책임이 아니다. + +## 언제 호출할지 + +- 사용자가 "지금 작업이 뭐지?", "현재 작업이 뭐야?", "어디까지 했지?"라고 물을 때 +- 사용자가 레포 전환 직후 로드맵상 현재 좌표를 빠르게 알고 싶어 할 때 +- "로드맵상 현 위치", "현재 마일스톤 위치", "current 기준 breadcrumb"를 요청할 때 +- 구현 시작 전 전체 Roadmap > Phase > Milestone 관계만 확인하면 될 때 + +## 먼저 확인할 것 + +- [ ] `agent-ops/skills/common/_templates/roadmap-position-report-template.md`를 읽어 최신 답변 템플릿 확인 +- [ ] `agent-roadmap/` 디렉터리 존재 여부 확인 +- [ ] 로컬 `agent-roadmap/current.md` 존재 여부 확인 +- [ ] 로드맵이 있으면 `ROADMAP.md`의 `Phase 흐름`을 확인 +- [ ] 로컬 `current.md`의 활성 Phase와 활성 Milestone 이름, 상태, 경로 확인 +- [ ] 활성 Phase 문서의 `Milestone 흐름` 확인 +- [ ] 활성 Milestone 문서의 제목, `목표`, `상태`만 확인 + +## 실행 절차 + +1. `agent-roadmap/` 존재 여부를 확인한다. + - 없으면 로드맵 없음으로 짧게 보고하고 멈춘다. +2. `agent-ops/skills/common/_templates/roadmap-position-report-template.md`를 읽는다. +3. 로컬 `agent-roadmap/current.md`를 확인한다. + - 없으면 로컬 current 없음으로 보고하고, `ROADMAP.md`가 있으면 `Phase 흐름`만 보여준다. +4. `ROADMAP.md`의 `Phase 흐름`을 읽어 전체 Phase 목록을 만든다. + - 각 Phase는 상태, 이름, 링크만 남긴다. + - current의 활성 Phase와 일치하는 항목에 `← 현재` 표시를 붙인다. + - 완료된 Phase가 archive 경로를 가리켜도 링크만 표시하고 archive 문서는 읽지 않는다. +5. current의 활성 Phase 경로를 열고 `Milestone 흐름`을 읽는다. + - 각 Milestone은 상태, 이름, 링크만 남긴다. + - current의 활성 Milestone과 일치하는 항목에 `← 현재 후보` 표시를 붙인다. +6. current의 활성 Milestone 문서를 열고 제목, `목표`, `상태`만 읽는다. + - `기능`, `완료 리뷰`, `범위 제외`, `작업 컨텍스트`는 사용자가 명시적으로 요청한 경우에만 읽는다. +7. 결과를 템플릿 형식 그대로 출력한다. + +## 실행 결과 검증 + +- [ ] `roadmap-position-report-template.md`의 출력 구조를 유지했는가 +- [ ] 로컬 `current.md`, `ROADMAP.md`, 활성 `PHASE.md`, 활성 Milestone의 제목/목표/상태만 기본으로 읽었는가 +- [ ] 완료 또는 archive Phase/Milestone은 링크만 표시하고 archive 문서를 읽지 않았는가 +- [ ] current가 여러 Milestone을 가리키면 모두 `현재 후보`로 표시했는가 +- [ ] 코드 파일, 테스트 파일, `git status`, `git diff`를 기본 동작에서 읽지 않았는가 +- [ ] 로드맵 파일을 수정하지 않았는가 + +## 출력 형식 + +- 템플릿 경로: `agent-ops/skills/common/_templates/roadmap-position-report-template.md` +- 템플릿을 그대로 복사해 placeholder를 채운다. +- 섹션 제목과 필드명을 임의로 번역, 축약, 삭제하지 않는다. +- current가 여러 Milestone을 가리키면 breadcrumb와 Milestone 흐름에 모두 표시한다. +- 현재 후보의 역할 태그는 `선행 스케치`, `다음 구현 계획`, `검토 후보`, `보류 후보`처럼 짧게 쓴다. +- 로드맵이 없는 프로젝트에서는 로드맵 없음으로 짧게 보고하고 템플릿을 억지로 채우지 않는다. + +## 금지 사항 + +- 기본 동작에서 코드 파일, 테스트 파일, `git status`, `git diff`를 읽지 않는다. +- 기본 동작에서 Milestone의 `기능` 체크리스트를 감사하지 않는다. +- 완료 여부, 남은 작업, 코드와 문서의 동기화 상태를 evidence 기반으로 판정하지 않는다. +- 사용자가 명시하지 않은 상태에서 `ROADMAP.md`, 로컬 `current.md`, Phase, Milestone 문서를 수정하지 않는다. +- 사용자가 과거 기록 확인을 명시하지 않으면 `agent-roadmap/archive/**`를 읽지 않는다. diff --git a/agent-ops/skills/common/check-roadmap-dependency/SKILL.md b/agent-ops/skills/common/check-roadmap-dependency/SKILL.md new file mode 100644 index 0000000..d11ae00 --- /dev/null +++ b/agent-ops/skills/common/check-roadmap-dependency/SKILL.md @@ -0,0 +1,91 @@ +--- +name: check-roadmap-dependency +version: 1.2.0 +description: roadmap dependency lock 상태를 확인한다. 사용자가 roadmap-dependency-checker.sh, locks.yaml, 외부 의존 잠금 충족 여부, unlock-ready 판별, 프로젝트 간 잠금 해제 조건 확인을 요청할 때 사용하며, 현재 Milestone이 locked 쪽인지 rely-on.target 쪽인지 양방향으로 판별한다. +--- + +# check-roadmap-dependency + +## 목적 + +`.agent-roadmap-sync/locks.yaml`에서 특정 lock `id`의 `locked`, `rely-on.target`, `rely-on.status`가 유효한지 확인하고, 모든 `rely-on.status`가 `enable`인지 판별한다. +lock id가 없으면 현재 Milestone identity가 `locked`에 있는지, `rely-on.target`에 있는지 모두 찾아 양방향으로 보고한다. +Milestone identity 역검색과 개별 lock 판별은 `agent-ops/bin/roadmap-dependency-checker.sh`에 맡기고, 스킬은 입력 확정, 실행, 결과 해석만 담당한다. + +## 언제 호출할지 + +- 사용자가 특정 roadmap lock id의 의존 조건 충족 여부를 확인할 때 +- runtime에서 unlock-ready 트리거 전 `locks.yaml` 판별 절차를 확인할 때 +- `.agent-roadmap-sync/locks.yaml`의 `rely-on.status` 기반 true/false 확인을 요청할 때 +- 사용자가 현재 Milestone이 다른 Milestone을 잠그는지 또는 다른 Milestone에 의해 잠겼는지 확인할 때 + +## 입력 + +- `lock-id`: 확인할 lock entry id. 예: `oto:oto-agent-registration`. 명시되면 이 id만 판별한다 (선택) +- `target-milestone`: 확인할 Milestone path 또는 `:agent-roadmap/phase/.../milestones/.md`. 없으면 현재 프로젝트 로컬 `current.md`의 활성 Milestone 단일 후보를 사용한다 (선택) +- `direction`: `locked` / `rely-on` / `both`. 기본값은 `both`다. `locked`는 대상 Milestone이 잠긴 entry만, `rely-on`은 대상 Milestone을 선행 조건으로 삼는 entry만 확인한다 (선택) +- `locks-file`: 기본값은 프로젝트 상위 `.agent-roadmap-sync/locks.yaml`이며, 다른 파일을 검사할 때만 지정한다 (선택) + +## 먼저 확인할 것 + +- [ ] `agent-ops/bin/roadmap-dependency-checker.sh`가 있는지 확인한다. +- [ ] `lock-id`가 명시되었거나 `target-milestone` 또는 현재 프로젝트 로컬 `current.md`의 활성 Milestone 하나로 Milestone identity를 추론 가능한지 확인한다. +- [ ] 기본 경로 또는 지정된 `locks-file`을 읽을 수 있는지 확인한다. + +## 실행 절차 + +1. **입력 확정** + - 사용자 요청에서 `lock-id`를 추출한다. + - `locks-file`이 명시되지 않았으면 프로젝트 상위 `.agent-roadmap-sync/locks.yaml`을 사용한다. + - `locks-file`이 없거나 읽을 수 없으면 결과를 `error`로 보고한다. 이를 미충족 의존성으로 해석하지 않는다. + - `lock-id`가 있으면 그 id만 판별한다. + - `lock-id`가 없으면 `target-milestone` 또는 현재 프로젝트 로컬 `agent-roadmap/current.md`의 활성 Milestone 단일 후보로 Milestone identity를 만든다. + - Milestone identity는 `:agent-roadmap/phase//milestones/.md` 형식이다. + - `target-milestone`이 `agent-roadmap/...` 상대 경로이면 현재 프로젝트명을 prefix로 붙인다. + - `target-milestone`이 workspace 하위 절대/상대 경로이면 workspace 바로 아래 디렉터리명을 project로 삼고, 그 뒤 `agent-roadmap/...` 경로를 붙인다. + - `target-milestone`이 archive 경로이면 일반 의존 판별 후보로 쓰지 말고 사용자에게 활성 Milestone 선택을 요청한다. + - 활성 Milestone 후보가 없거나 둘 이상이면 추정하지 말고 사용자에게 lock id 또는 Milestone 선택을 요청한다. + - `lock-id`가 없으면 `agent-ops/bin/roadmap-dependency-checker.sh --find-milestone "" "" ""`로 Milestone identity와 일치하는 entry를 찾는다. + - find 출력의 `locked:`는 locked entries로, `rely-on:`는 rely-on entries로 분류한다. + - find 출력이 `none`이면 결과를 `error`가 아니라 `no-lock`으로 보고한다. + +2. **판별 실행** + - 확인 대상 lock id마다 `agent-ops/bin/roadmap-dependency-checker.sh "" ""`를 실행한다. + - `locked` 방향의 결과는 대상 Milestone의 잠금 해제 조건 충족 여부다. + - `rely-on` 방향의 결과는 대상 Milestone이 완료될 때 영향을 받는 downstream lock의 현재 충족 여부다. + +3. **결과 해석** + - stdout `true`와 exit code `0`은 모든 의존 조건 충족으로 보고한다. + - stdout `false`와 exit code `1`은 아직 충족되지 않은 의존 조건이 있음을 보고한다. + - exit code `2`는 설정 오류, 입력 오류, 필수 필드 누락, 파싱 오류로 보고한다. + - `rely-on` 방향에서 대상 Milestone이 `[검토중]` 또는 `[완료]`인데 status가 `disable`이면 `update-roadmap` 동기화가 필요한 stale lock으로 보고한다. 이 스킬은 status를 바꾸지 않는다. + +## 실행 결과 검증 + +- [ ] 각 checker 실행의 stdout이 `true` 또는 `false` 하나로만 출력되었는가 +- [ ] exit code `0`, `1`, `2` 의미를 구분해 보고했는가 +- [ ] exit code `2`를 unlock 대기 상태로 해석하지 않았는가 +- [ ] lock id가 없을 때 대상 Milestone을 `locked`와 `rely-on.target` 양쪽에서 찾았는가 +- [ ] `locks.yaml`이 없는 경우 `false`가 아니라 `error`로 보고했는가 +- [ ] 이 스킬이 `locks.yaml`이나 roadmap 문서를 수정하지 않았는가 +- 검증 실패 시: 오류 메시지와 사용한 `lock-id`, `locks-file` 경로를 보고하고 멈춘다. + +## 출력 형식 + +```markdown +## 의존성 판별 결과 + +- target-milestone: `` +- direction: `` +- locks-file: `` +- 결과: `` +- locked entries: `` +- rely-on entries: `` +- 의미: <모든 의존 조건 충족 | 미충족 의존 조건 있음 | 설정/입력/필수 필드/파싱 오류 | 대상 Milestone 관련 lock 없음> +``` + +## 금지 사항 + +- `locks.yaml`의 `status`를 변경하지 않는다. +- roadmap Milestone의 `구현 잠금` 상태를 변경하지 않는다. +- `false`와 설정 오류를 같은 상태로 보고하지 않는다. diff --git a/agent-ops/skills/common/code-review/SKILL.md b/agent-ops/skills/common/code-review/SKILL.md new file mode 100644 index 0000000..ba444f2 --- /dev/null +++ b/agent-ops/skills/common/code-review/SKILL.md @@ -0,0 +1,455 @@ +--- +name: code-review +description: Use for active task review requests such as 리뷰 진행해, 리뷰해줘, 코드 리뷰해줘, code review, CODE_REVIEW.md, USER_REVIEW.md resolution, implementation-time 사용자 리뷰 요청, or 리뷰 루프. Review the active PLAN/CODE_REVIEW pair, append PASS/WARN/FAIL, archive both active files, and create the required next state: PASS writes complete.log and moves the task to archive; WARN/FAIL writes follow-up PLAN/CODE_REVIEW files unless the user-review gate requires USER_REVIEW.md; user review resolution that closes the task writes complete.log before archive. Never stop after verdict append or user-review closure without writing the required next-state artifact. +--- + +# Code Review + +## Purpose + +Review the implementation phase of the plan-code-review loop: + +```text +plan skill -> implementation -> code-review skill + ^ | + +----- issues found: new routed plan/review files +``` + +Implementation may stop early by filling the active review stub's `사용자 리뷰 요청` section from `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`. This is the only implementation-time user-decision path: implementation must not ask the user directly, present choices in chat, or call a user-input tool such as `request_user_input`. Code-review validates that request and, when justified, writes `USER_REVIEW.md` from `agent-ops/skills/common/code-review/templates/user-review-template.md`. + +## Core Loop Rules + +- Trigger: Korean or English active-task review requests, including `리뷰 진행해` and `리뷰해줘`, must use this skill when an active `CODE_REVIEW-*-G??.md` or `USER_REVIEW.md` exists under `agent-task/*/` or `agent-task/*/*/`, excluding `agent-task/archive/**`. +- Finalize every selected active state: for `CODE_REVIEW-*-G??.md`, append one verdict, archive the active review and plan files, then create exactly one next state; for `USER_REVIEW.md` completion, update the stop state, write `complete.log`, and archive the task. Do not report before finalization is complete. +- Next state: `PASS` writes `complete.log` and moves the task under `agent-task/archive/YYYY/MM/`; if the task group is `m-`, report completion metadata for the runtime event. `WARN` or `FAIL` normally writes the next active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md`; if the user-review gate triggers, write `USER_REVIEW.md` instead. A completed `USER_REVIEW.md` uses the same terminal `complete.log` and archive path as `PASS`. +- A filled implementation-owned `사용자 리뷰 요청` is a review input, not a next state by itself. If it is a genuine user-only decision, user-owned external environment prerequisite, or scope conflict, the user-review gate triggers and this skill writes `USER_REVIEW.md`; if it is vague, unsupported, repo-fixable, or only missing evidence that a follow-up agent can produce by rerunning commands or collecting artifacts, write a normal WARN/FAIL follow-up plan instead. +- Do not replace `USER_REVIEW.md` with an inline user question. When the user-review gate triggers, write the file-based stop state and report its path. +- Do not ask for confirmation before WARN/FAIL follow-up files. If the user-review gate triggers, write `USER_REVIEW.md`; otherwise, if details are uncertain, write the smallest concrete follow-up plan with file references and verification commands. +- Recovery: if a prior turn appended a verdict without archive or next-state files, or resolved `USER_REVIEW.md` without `complete.log`, resume at the matching finalization step and finish the missing artifacts first. + +## User Review Gate + +`USER_REVIEW.md` is a loop stop state for loop-limit exhaustion or user-owned blockers. Default to a normal WARN/FAIL follow-up; the gate requires positive evidence that another implementation/review agent cannot resolve the blocker without user action. + +- Compute `review-number` as `count(agent-task/{task_name}/code_review_*.log) + 1` before archiving the active review. +- If `review-number >= 5` and the verdict is `WARN` or `FAIL`, stop the automatic loop and write `USER_REVIEW.md`. +- If `review-number == 1`, the verdict is `FAIL`, and the blocking issue is a test environment problem that requires user action rather than repo-owned setup or evidence a follow-up agent can produce by rerunning commands or collecting artifacts, stop the automatic loop and write `USER_REVIEW.md`. +- If the active review file has a filled `사용자 리뷰 요청` with `상태` other than `없음`, and the blocker is a user-only decision, user-owned external environment prerequisite, or scope conflict that the repository and a capable next agent cannot safely resolve by code changes or verification reruns, stop the automatic loop and write `USER_REVIEW.md`. +- If the active review file records a decision the implementer would otherwise ask as chat choices, treat it as an `implementation-blocked` user-review candidate when it has the required evidence and resume condition; do not ask that choice inline during review. +- Treat a first-review failure as a test environment problem only with evidence from an attempted command: missing SDK/tool/runtime, unavailable external service, absent secret/credential, unsupported OS/device/interactive environment, or another prerequisite outside the repository. Record the exact command and stdout/stderr. Before claiming a tool is missing, require `command -v ` or the project-equivalent check. Missing or non-verbatim verification evidence is a verification-trust failure, not a test environment problem. +- Treat an implementation-owned `사용자 리뷰 요청` as valid only with specific decision questions, evidence, attempted commands/output or a reason commands could not run, and clear resume conditions. The not-run reason must identify user-owned action or an external prerequisite; agent execution limits, missing handoff evidence, or incomplete verification records become normal WARN/FAIL follow-up plans when a capable follow-up lane can produce the evidence. +- Do not classify repo-owned setup bugs or verification evidence gaps as environment problems. If a manifest, bootstrap script, test fixture, documented project entrypoint, or follow-up agent can provide the dependency or evidence, write a normal follow-up plan instead. +- `USER_REVIEW.md` is created from `agent-ops/skills/common/code-review/templates/user-review-template.md`, filled with the archived loop history, current archived plan/review paths, verdict, loop count, blocking evidence, and user decision options. + +## User Review Resolution + +When an active `USER_REVIEW.md` exists and the user decision closes the task as complete/PASS, finalization is still owned by this skill. + +- Read `USER_REVIEW.md`, archived `plan_*.log`, and archived `code_review_*.log` in that task directory. +- Verify the user decision and any follow-up evidence are sufficient to close the task. If a new implementation plan is needed instead, do not close; route back to the plan skill, which archives `USER_REVIEW.md` to `user_review_N.log` before writing a new plan. +- Update `USER_REVIEW.md` in place to show a resolved state, final verdict, loop history, fulfilled decision items, and the evidence that closed the stop state. +- Write `complete.log` from `agent-ops/skills/common/code-review/templates/complete-log-template.md` before moving or archiving the task directory. Include both the original archived review verdict and the user-review resolution line in `루프 이력`. +- Then apply the same task-directory archive move and `m-` PASS completion metadata rules as a normal `PASS`. +- Do not leave an active task directory that contains `USER_REVIEW.md` and `*.log` files but no `complete.log` after the user has completed and approved the task. + +## Workflow Contract + +Active work must live under an active task directory using routed filenames. This is the state protocol shared with the plan skill. + +Task path terms: + +- `{task_group}` is the top-level work category under `agent-task/`. Normal task groups use a short snake_case name such as `refactoring`. +- Milestone-linked work uses the reserved task group form `m-`, where `` is the active Milestone filename without `.md`. +- `{subtask_dir}` is used only for split work and follows the indexed directory naming contract, such as `01_core` or `02+01_db`. +- `{subtask_name}` is the short snake_case name after the index or dependency prefix inside `{subtask_dir}`. +- `{task_name}` in headers and templates means the active task path relative to `agent-task/`: either `{task_group}` for a single-plan task or `{task_group}/{subtask_dir}` for a split subtask. +- A single-plan task stores active files directly under `agent-task/{task_group}/`. +- Split work stores active files under `agent-task/{task_group}/{subtask_dir}/`; the parent `agent-task/{task_group}/` is only the grouping folder and must not contain active plan/review files. + +Filename rules: + +- Plan file: `PLAN-{build_lane}-GNN.md` +- Review file: `CODE_REVIEW-{review_lane}-GNN.md` +- `{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 subtask directories under one shared `{task_group}`. Each subtask directory owns exactly one normal active plan file and one normal active review file. +- Multi-plan subtask directory names encode runtime scheduling metadata: + - `NN_{subtask_name}` has no runtime dependencies. + - `NN+PP[,QQ...]_{subtask_name}` depends on the listed earlier task indices. +- Subtask directory names are the runtime dependency source of truth. Preserve them verbatim; do not normalize, reinterpret, infer extra dependencies from numeric order, or choose execution order by agent judgment. +- If the user/runtime names a task group, task path, or subtask directory that identifies exactly one active review file, review that directory even when other active review files exist. + +Milestone task group contract: + +- `agent-task/m-/` is reserved for Milestone-linked work created by the plan skill. +- Do not treat normal task groups that do not start with `m-` as runtime milestone completion targets. +- For a selected task path, parse only the first path segment as `{task_group}`. If it matches `^m-[a-z0-9][a-z0-9-]*$`, strip `m-` to get ``. +- Do not read or modify `agent-roadmap/**` for milestone routing during code-review finalization. +- Do not call `update-roadmap` from this skill. The runtime consumes the PASS completion event, checks current state, resolves the active Milestone, and calls `update-roadmap` if needed. +- For `m-` PASS tasks, report the original active task path, final archive path, complete log path, task group, and milestone slug so the runtime has deterministic event inputs. + +Review routing rules: + +- Route reviews so a normal mixed backlog can use both lanes substantially. This is not a per-review quota; neither `local` nor `cloud` is the default catch-all. +- `local`: first-pass and review-detectable work, including multi-file or API/call-site reviews when scope and required context are bounded and verification can be rerun or reasoned about locally. +- `local-G01` through `local-G10` and `cloud-G01` through `cloud-G10` are valid. Do not treat high grade alone as an implicit cloud route; context volume is separate lane evidence. +- Repair non-behavioral review artifact drift during review instead of failing solely for it when implementation correctness, tests, and contracts remain judgeable. +- `cloud`: reviews requiring stronger-than-local judgment, larger-than-local context, or evidence recovery: weak verification that review cannot compensate for, security/auth, storage/migration, concurrency, protocol/schema, broad cross-domain behavior with hard-to-review failure modes, repeated non-trivial Required issues, or untrusted verification evidence. +- `cloud-G07` or higher is mandatory only when terminal-agent or external interactive behavior is central and cannot be made deterministic for local review, or when the review requires more source/test/diff/log context than local can hold without omitting material evidence: shell/CLI workflow implementation, bin script orchestration, process control, stdout/stderr parsing, exit-status contracts, long-running command diagnosis, terminal benchmark-style tasks, real bin/smoke/integration failures after unit tests passed, TUI/PTY/browser/screen repaint/cursor stream behavior, rerun-mismatched verification trust failures, or very large cross-module/cross-domain changes that cannot be safely split for review. + +Directory states: + +| State | Meaning | +|-------|---------| +| `PLAN-*-G??.md` + unfilled `CODE_REVIEW-*-G??.md` stub/placeholders | Implementation is not judgeable; review should fail completeness if invoked | +| `PLAN-*-G??.md` + `CODE_REVIEW-*-G??.md` with filled `사용자 리뷰 요청` | Implementation claims a user-only blocker; review validates the request and writes `USER_REVIEW.md` only if justified | +| `PLAN-*-G??.md` + filled `CODE_REVIEW-*-G??.md` | Ready for code-review skill | +| `complete.log` + `*.log` files | Task complete (PASS or user-review-resolved PASS), before final task-directory archive move | +| `USER_REVIEW.md` + `*.log` files | Automatic loop stopped; user decision is required before creating another plan | +| `agent-task/archive/YYYY/MM/{task_name}/complete.log` + `*.log` files | Archived completed task path (PASS or user-review-resolved PASS); not active | +| 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. + +## Step 1 - Find Active Task + +Find active review files with both globs, excluding `agent-task/archive/**`: + +- `agent-task/*/CODE_REVIEW-*-G??.md` +- `agent-task/*/*/CODE_REVIEW-*-G??.md` + +Also note active user-review stops, excluding `agent-task/archive/**`: + +- `agent-task/*/USER_REVIEW.md` +- `agent-task/*/*/USER_REVIEW.md` + +Classify the combined set of active `CODE_REVIEW-*-G??.md` and `USER_REVIEW.md` paths. Apply the first matching row: + +| Result | Action | +|--------|--------| +| Exactly one active path and it is `CODE_REVIEW-*-G??.md` | Review that task; exactly one `PLAN-*-G??.md` is expected beside it. | +| Exactly one active path and it is `USER_REVIEW.md`, with a user-supplied completion/resolution decision | Perform User Review Resolution for that task. | +| One or more active paths and every active path is `USER_REVIEW.md`, with no completion/resolution decision | Report that user decision is required and list the paths. | +| No active paths | Stop and report that no active review exists. | +| Multiple active paths | If the user/runtime named a task group, task path, or subtask directory that identifies exactly one active path, use that directory. Otherwise list paths and ask which task to review or resolve; do not choose by agent judgment. | + +If a selected task directory contains both `USER_REVIEW.md` and active `PLAN-*-G??.md` or `CODE_REVIEW-*-G??.md`, report an inconsistent loop state instead of overwriting either state. + +## Step 2 - Load Context + +Count `agent-task/{task_name}/code_review_*.log` in the selected active task directory: + +- `0`: first review. Read the active review file, active plan file, every planned source file, related tests, and files importing/imported by changed files up to 2 levels deep. +- `>=1`: follow-up review. Start with `git diff`, `git diff --cached`, and `git log --oneline -5`, then expand to related callers, implementers, tests, and any planned files missing from the diff. + +The diff is the starting point, not the boundary. Follow behavior and API connections far enough to judge correctness. + +## Step 3 - Pre-Review Checklist + +Before writing the verdict: + +- Compare actual source files against every planned checklist item. +- Compare the plan `구현 체크리스트` and review stub `구현 체크리스트`; repair non-behavioral drift when implementation remains judgeable. +- Directly repair obvious non-behavioral source nits when safe: typos, stale comments, docs, or formatting only, with no behavior/test/API contract change. +- If a checklist item contains integrated verification for a feature, treat that feature item as incomplete until both implementation evidence and the matching verification output are present. Do not accept a separate unchecked completion-criteria item as a substitute. +- Confirm the implementation marked the matching checklist items in the active review file, including the final mandatory `CODE_REVIEW-*-G??.md` completion item; repair clear artifact drift when evidence supports completion. +- Treat review artifact gaps as failures only when they prevent judging implementation correctness, tests, contracts, or verification trust. +- Read the `사용자 리뷰 요청` section. If `상태` is not `없음`, validate the exact decision needed, blocker evidence, command output or not-run reason, why automatic follow-up cannot resolve it, and resume condition before deciding whether the user-review gate triggers. +- 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. +- For follow-up reviews, compare diff against the plan and scan for unplanned changes, debug prints, dead code, TODOs, formatting-only noise, and unrelated edits. + +## Step 4 - Append Verdict + +Append `코드리뷰 결과` to the active `CODE_REVIEW-*-G??.md`. + +Required fields: + +- `종합 판정`: exactly `PASS`, `WARN`, or `FAIL`. +- `차원별 평가`: Pass/Warn/Fail for correctness, completeness, test coverage, API contract, code quality, plan deviation, verification trust. +- `발견된 문제`: `없음`, or bullets using `Required`, `Suggested`, or `Nit` with `file:line` and a concrete fix. +- `다음 단계`: keep only the matching PASS, WARN/FAIL follow-up, or USER_REVIEW line. + +Do not check archive/next-state items in `코드리뷰 전용 체크리스트` during Step 4. Complete the applicable dedicated checklist items in the archived `code_review_*.log` during Step 7, after archive, next-state writes, and PASS task-directory moves are done. + +Severity semantics: + +| Verdict | Meaning | Follow-up plan | +|---------|---------|----------------| +| `PASS` | No Required/Suggested issues. Nit-only findings may still PASS. | No | +| `WARN` | One or more Suggested issues, zero Required. | Yes, unless the user-review gate triggers | +| `FAIL` | One or more Required issues. | Yes, unless the user-review gate triggers | + +Issue severity: + +- `Required`: correctness, API contract, missing required test, missing integrated verification, plan-completeness issue, or review artifact gaps that prevent judging implementation quality. +- `Suggested`: useful improvement that should enter the loop but does not block correctness. +- `Nit`: tiny cleanup; directly repair obvious non-behavioral cases when safe, otherwise record 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 is mandatory for `PASS`, `WARN`, and `FAIL`. Before the archive order, ensure `.gitignore` has the Agent-Ops managed gitignore block for task artifacts before writing `*.log` outputs. Prefer `source agent-ops/bin/ai-ignore.sh && agent_ops_ensure_gitignore_task_artifact_block .gitignore`; if the helper is unavailable, add or update a block containing `!agent-task/`, `!agent-task/**/`, `!agent-task/**/*.md`, `!agent-task/**/*.log`, and `agent-roadmap/current.md`. + +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`. + +After archiving, neither active `.md` file remains unless Step 6 writes a follow-up plan/review or `USER_REVIEW.md`. + +## Step 6 - Post-Review Actions + +For `PASS`, write `agent-task/{task_name}/complete.log` before reporting. If a `USER_REVIEW.md` stop is resolved as complete/PASS, write the same `complete.log` before archiving that task. + +Complete log template: + +- Template path: `agent-ops/skills/common/code-review/templates/complete-log-template.md` +- Copy the template's section order and fill every placeholder from the archived plan/review logs and final verdict. +- Do not leave placeholders in `complete.log`. +- If the task did not close through `USER_REVIEW.md`, remove the optional user-review row from the `루프 이력` table. +- If the archived plan or review log contains `Roadmap Targets`, copy it into `complete.log` as `Roadmap Completion`. Include the Milestone path, completed Task ids, archived plan/review log paths, and verification evidence. If there is no `Roadmap Targets` section, remove the optional `Roadmap Completion` template section entirely and do not invent roadmap targets. +- Use `없음` for empty `잔여 Nit` or `후속 작업`. +- A PASS `complete.log` must not contain unresolved Required or Suggested issues. Nit-only leftovers may be recorded under `잔여 Nit`. + +For `WARN` or `FAIL`, apply the user-review gate before writing follow-up files. + +If the user-review gate triggers: + +- Write `agent-task/{task_name}/USER_REVIEW.md` from `agent-ops/skills/common/code-review/templates/user-review-template.md`. +- Fill every placeholder. Use `loop-limit`, `environment-blocked`, or `implementation-blocked` as the reason type. +- Include all available archived loop history in chronological order, the current archived plan/review log paths, the final verdict, review number, issue summary, blocking evidence, and the exact user decision needed. +- Do not write a new active `PLAN-{build_lane}-GNN.md` or `CODE_REVIEW-{review_lane}-GNN.md`. +- Do not write `complete.log`. +- If the task group is `m-`, report that the milestone task is blocked on user review; do not emit PASS completion metadata and do not call `update-roadmap`. + +If the user-review gate does not trigger, write new routed plan/review files using the plan skill format: + +- New plan number is the count of `plan_*.log` after archive. +- Header tag is `REVIEW_`. +- If the selected task group is `m-`, write the follow-up plan/review under the same `m-` task group. +- Base the follow-up scope directly on the archived review findings. Keep it narrow and actionable. +- Choose lane/grade again; preserve the prior route when it was adequate. Raise `local-GNN` for explicit, deterministic, review-detectable follow-ups, but move `local -> cloud` when the failure exposes reasoning, risk, evidence, or context-volume gaps that local review should not own. +- If the finding is only missing or untrusted verification evidence that an appropriate follow-up lane can produce by rerunning commands or collecting artifacts, do not write `USER_REVIEW.md` solely for that reason. Write a follow-up plan that reruns the exact commands in an appropriate lane and records actual stdout/stderr. +- If follow-up verification involves mobile/UI hangs, timeouts, or no progress for 2 minutes, stop blind retries; require focused rerun output plus screenshot/window/UI-tree evidence path, or the exact reason capture is impossible. +- Before choosing the follow-up route, apply this escalation gate: + - If the archived plan was `local-*` and the verdict is `FAIL` for correctness, completeness, test coverage, or verification trust, keep the follow-up on `local` with a higher grade when the issue is explicit, deterministic, and review-detectable; move to `cloud` only when the failure shows local capability or evidence trust is likely insufficient. + - If the follow-up work is terminal-agent work that cannot be reduced to deterministic commands and evidence, use `cloud-G07` or higher. + - If unit tests passed but a real bin/smoke/integration command failed and local follow-up cannot isolate the failure with deterministic evidence, use `cloud-G07` or higher. + - If the task depends on interactive TUI/PTY/browser/external CLI behavior, screen repaint/cursor stream parsing, or live command-palette state that cannot be captured deterministically, use `cloud-G07` or higher. + - If the follow-up requires more source/test/diff/log context than local can hold without dropping material evidence, or the failure spans broad cross-module/cross-domain context that cannot be safely split, use `cloud-G07` or higher. + - If verification evidence is missing, reconstructed, stale, untrusted, or mismatched on rerun, use a local follow-up only when exact commands and actual stdout/stderr capture are enough; use `cloud-G07` or higher when evidence trust recovery needs stronger diagnosis. +- `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. +- The follow-up plan's implementer instructions and review stub must forbid direct user prompts and include the implementation-time `사용자 리뷰 요청` stop path so the next implementer can act without reading the plan skill. +- Copy `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` into the follow-up review stub's `사용자 리뷰 요청` section. +- If the archived plan had `Roadmap Targets` and the follow-up still works toward the same Milestone Task completion, copy that section into the follow-up plan and review stub. If the follow-up intentionally no longer claims roadmap Task completion, omit the section and explain why in the follow-up plan `범위 결정 근거`. + +Routed review stub template (fill `{…}` placeholders; everything else is fixed and must not be changed by the implementing agent): + +```markdown + + +# 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. +> Fill implementation-owned sections, then stop with active files in place and report ready for review. +> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves. +> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review. +> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume. +> Follow the ownership table at the bottom of this file for which sections you own. + +## 개요 + +date={YYYY-MM-DD} +task={task_name}, plan={N}, tag={TAG} + +{copy the follow-up plan's Roadmap Targets section exactly if present; omit this block if the follow-up plan has no Roadmap Targets} + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. + +1. 판정을 append한다. +2. `CODE_REVIEW-{review_lane}-GNN.md` → `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` → `plan_{build_lane}_GNN_M.log`로 아카이브한다. +3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다. +4. PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [{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`로 아카이브한다. +- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. +- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. +- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다. +- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +{pre-filled from plan — one bullet per review focus area} + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### {TAG}-1 중간 검증 +``` +$ {verification command from plan} +(output) +``` + +### 최종 검증 +``` +$ {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 review-agent-only sections unchanged. +``` + +Sections and their ownership: + +| 섹션 | 소유자 | 설명 | +|------|--------|------| +| 헤더 주석, 개요(date/task/plan/tag), 리뷰 에이전트 지시 | 스텁 생성 시 고정 | 구현 에이전트가 수정하거나 실행하지 않음 | +| Roadmap Targets | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 수정하지 않음; PASS 시 code-review가 `complete.log`의 `Roadmap Completion`으로 복사 | +| 구현 항목별 완료 여부 (항목명) | 스텁 생성 시 고정 | `[ ]` → `[x]` 체크만 구현 에이전트가 수행 | +| 구현 체크리스트 (항목 텍스트/순서) | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 `[ ]` → `[x]` 체크만 수행; 마지막 체크박스는 저장 전 필수 | +| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section | +| 계획 대비 변경 사항, 주요 설계 결정 | 구현 에이전트가 채움 | placeholder 텍스트를 실제 내용으로 교체 | +| 사용자 리뷰 요청 | 구현 에이전트가 채움 | 진행에 사용자 입력이 필요하지 않으면 `상태: 없음` 유지; 구현 중 직접 질문은 금지; 필요하면 결정 항목, 근거, 명령 출력, 자동 후속 불가 이유, 재개 조건을 기록 | +| 리뷰어를 위한 체크포인트 | 스텁 생성 시 고정 | 계획에서 추출한 리뷰 포인트 | +| 검증 결과 (섹션 제목 + 명령) | 스텁 생성 시 고정 | 실행 출력만 구현 에이전트가 채움; 명령 변경은 `계획 대비 변경 사항`에 기록 | +| 코드리뷰 결과 | 리뷰 에이전트가 append | 스텁에 포함하지 않음 | + +## Step 7 - Complete Review-Only Checklist, Move PASS Task, And Report + +After Step 6: + +- If verdict is `PASS`, determine archive month from the current completion date as `YYYY/MM`, create the needed archive parent directories, then move the selected active task directory `agent-task/{task_name}/` to `agent-task/archive/YYYY/MM/{task_name}/`. For split work, the selected active task directory is the subtask directory, and the archive must preserve the task group path, e.g. `agent-task/refactoring/01_core/` moves to `agent-task/archive/YYYY/MM/refactoring/01_core/`. +- Do not overwrite an existing archive directory. If `agent-task/archive/YYYY/MM/{task_name}/` already exists, append the next numeric suffix to the final path segment: single-plan `agent-task/archive/YYYY/MM/{task_group}_1/`, split-plan `agent-task/archive/YYYY/MM/{task_group}/{subtask_dir}_1/`, and so on. +- After moving a split subtask, remove the active parent `agent-task/{task_group}/` only if it is empty. If sibling subtask directories or other files remain, leave the parent task group in place. +- If verdict is `PASS` and `{task_group}` matches `m-`, do not resolve the roadmap target and do not call `update-roadmap`. Report completion event metadata after the task archive move: `origin-task=agent-task/{task_name}` from the original active task path, `task-group={task_group}`, `milestone-slug=`, final archive path, `complete.log` path, archived plan/review log paths, and `roadmap-completion=`. +- The runtime consumes that completion event, checks current state, and calls `update-roadmap` if needed. `update-roadmap` only checks Milestone Task ids when `complete.log` contains `Roadmap Completion`; if the section is absent, roadmap Task completion is a no-op even for `m-*` task groups. +- `WARN` and `FAIL` do not update the roadmap Milestone; the follow-up plan remains under the same `m-` task group when the original task was Milestone-linked. +- `USER_REVIEW` does not update the roadmap Milestone and does not produce PASS completion metadata. Keep the active task directory in place with `USER_REVIEW.md` and archived plan/review logs until the user decides how to resume, replan, defer, or abandon the task. +- If `USER_REVIEW.md` is later resolved as complete/PASS by user decision and evidence, write `complete.log`, move the task directory to archive, and report `m-*` PASS completion metadata just like a normal `PASS`. +- For `PASS`, open the moved `agent-task/archive/YYYY/MM/{final_task_name}/code_review_{review_lane}_GNN_N.log`, where `{final_task_name}` is the archived task path, including `{task_group}/` for split work. +- For user-review-resolved PASS, confirm the moved archive contains the resolved `USER_REVIEW.md`, `complete.log`, and the existing archived `plan_*.log` / `code_review_*.log`; do not recreate an active review file only to add a new checklist item. +- For `WARN` or `FAIL`, open `agent-task/{task_name}/code_review_{review_lane}_GNN_N.log`. +- Run `git check-ignore -q --` on the generated task artifacts (`plan_*.log`, `code_review_*.log`, `user_review_*.log` when present, `complete.log` when present, and active follow-up `.md` files). If any are ignored, apply the Agent-Ops managed gitignore block and re-check before reporting. +- Check every applicable item in `코드리뷰 전용 체크리스트`; leave mutually exclusive verdict items unchecked. +- If any applicable item cannot be checked, finish the missing archive, `complete.log`, task-directory move, follow-up plan/review write, or `USER_REVIEW.md` 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, applicable checked review-only checklist, required next-state files, for `PASS` or user-review-resolved PASS the final task archive move, for `m-*` PASS tasks the completion event metadata, and for unresolved `USER_REVIEW` the filled `USER_REVIEW.md`. + +Report Required/Suggested counts, archive names, the final task archive path for `PASS`, the new plan path for normal `WARN`/`FAIL`, the `USER_REVIEW.md` path for user review stops, and any `m-*` runtime completion event metadata. + +## Review Dimensions + +| Dimension | Check | +|-----------|-------| +| Correctness | Logic, edge cases, concurrency, errors | +| Completeness | Planned implementation/verification items are done; review artifact drift is repaired when judgeable | +| Test coverage | Required tests present and meaningful | +| API contract | Call sites, compatibility, docs | +| Code quality | No debug prints, dead code, leftover TODOs | +| Plan deviation | Deviations justified, no unrelated risk | +| Verification trust | Reported output matches actual code | + +## Quality Rules + +- Lead with findings; use specific `file:line`. +- Provide a concrete fix for every Required issue. +- Name exact stale symbols or missing tests. +- Do not write vague praise or style opinions without a rule. +- Every dimension gets Pass/Warn/Fail. +- For follow-up plans about verification trust, specify deterministic commands, for example `rg --sort path`, and forbid repo-local tool artifacts. + +## Final Checklist + +- `code_review_{review_lane}_GNN_N.log` exists with verdict appended. +- `plan_{build_lane}_GNN_M.log` exists. +- `.gitignore` unignores `agent-task/**/*.md` and `agent-task/**/*.log` and ignores local `agent-roadmap/current.md`; generated task artifacts are not ignored by `git check-ignore`. +- No active `.md` files remain after PASS or user-review-resolved PASS. +- PASS or user-review-resolved PASS: `complete.log` written from `agent-ops/skills/common/code-review/templates/complete-log-template.md`, then task directory moved under `agent-task/archive/YYYY/MM/` with task-group path preserved for split work. +- PASS milestone task group: `m-` completion event metadata was reported for runtime; roadmap was not modified by code-review. +- PASS with `Roadmap Targets`: `complete.log` contains `Roadmap Completion` with Milestone path, Task ids, archived plan/review evidence, and verification evidence. +- PASS without `Roadmap Targets`: `complete.log` omits `Roadmap Completion` and reported metadata says `roadmap-completion=none`. +- PASS split: empty active parent `agent-task/{task_group}/` removed after the subtask move; non-empty parent left in place. +- WARN/FAIL without user-review gate: new active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md` created with matching headers and matching `구현 체크리스트`; no `complete.log`. +- Follow-up review stubs include the `사용자 리뷰 요청` section from `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` and forbid direct user prompts during implementation. +- USER_REVIEW: `USER_REVIEW.md` exists from template, no active `PLAN-*.md` or `CODE_REVIEW-*.md` remains, and no `complete.log` was written. +- Implementation-requested USER_REVIEW: archived review log preserves the filled `사용자 리뷰 요청` evidence and the generated `USER_REVIEW.md` records the exact user decision needed. +- USER_REVIEW resolved as PASS: archived task contains both resolved `USER_REVIEW.md` and `complete.log`. +- The applicable review-agent-only finalization checklist was completed before reporting. diff --git a/agent-ops/skills/common/code-review/agents/openai.yaml b/agent-ops/skills/common/code-review/agents/openai.yaml new file mode 100644 index 0000000..0ef4d88 --- /dev/null +++ b/agent-ops/skills/common/code-review/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Code Review" + short_description: "Review and route task loops" + default_prompt: "Use $code-review to review or resolve the active repository task, archive active files, and create complete.log, follow-up plan/review files, or USER_REVIEW.md." diff --git a/agent-ops/skills/common/code-review/templates/complete-log-template.md b/agent-ops/skills/common/code-review/templates/complete-log-template.md new file mode 100644 index 0000000..cadf8d7 --- /dev/null +++ b/agent-ops/skills/common/code-review/templates/complete-log-template.md @@ -0,0 +1,41 @@ +# Complete - {task_name} + +## 완료 일시 + +{YYYY-MM-DD or ISO-8601} + +## 요약 + +{one-line task summary, loop count, and final verdict; include user-review resolution if applicable} + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_{build_lane}_GNN_N.log` | `code_review_{review_lane}_GNN_N.log` | PASS/WARN/FAIL | {main outcome or follow-up reason} | +| `USER_REVIEW.md` | user decision | PASS/RESOLVED | {only when a user-review stop was resolved as the terminal completion path; remove this row otherwise} | + +## 구현/정리 내용 + +- {implemented or cleaned-up change} + +## 최종 검증 + +- `{command}` - {PASS/FAIL/BLOCKED}; {actual output summary or saved output path} + +## Roadmap Completion + +{optional; include only when archived plan/review had Roadmap Targets. Remove this entire section when there are no Roadmap Targets.} + +- Milestone: `{agent-roadmap/phase//milestones/.md}` +- Completed task ids: + - `{task-id}`: PASS; evidence=`{archived-plan-log}`, `{archived-review-log}`; verification=`{command or saved output path}` +- Not completed task ids: 없음 + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-ops/skills/common/code-review/templates/user-review-template.md b/agent-ops/skills/common/code-review/templates/user-review-template.md new file mode 100644 index 0000000..70ff03b --- /dev/null +++ b/agent-ops/skills/common/code-review/templates/user-review-template.md @@ -0,0 +1,52 @@ +# User Review Required - {task_name} + +## 요청 일시 + +{YYYY-MM-DD or ISO-8601} + +## 상태 + +USER_REVIEW + +## 사유 + +- 유형: {loop-limit, environment-blocked, or implementation-blocked} +- 현재 리뷰 회차: {review-number} +- 최종 판정: {WARN or FAIL} +- 요약: {why automatic follow-up stopped} + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `{plan-log-0}` | `{code-review-log-0}` | {PASS/WARN/FAIL/unknown} | {main issue or blocking reason} | +| `{current-archived-plan-log}` | `{current-archived-review-log}` | {WARN/FAIL} | {main issue or blocking reason} | + +## 차단 근거 + +- 문제: {review finding summary} +- 현재 archive plan: `{current-archived-plan-log}` +- 현재 archive review: `{current-archived-review-log}` +- 검증 명령: `{command or 없음}` +- 실제 출력: {stdout/stderr excerpt or saved output path} +- 차단 판단 근거: {why this needs user decision or is outside repo-fixable scope, or 없음} + +## 사용자 결정 필요 + +- [ ] 자동 follow-up plan/review를 계속 진행한다. +- [ ] 계획을 재작성한다. +- [ ] 테스트 환경, secret, 외부 서비스, SDK, 장비 조건을 준비한 뒤 재시도한다. +- [ ] 작업 범위를 줄이거나 보류/폐기한다. + +## 재개 조건 + +- {what must be true before the next plan/review loop should start} + +## 다음 실행 힌트 + +- {suggested command, task path, or user decision needed to resume} + +## 종료 규칙 + +- 사용자가 이 stop state를 완료/PASS로 해소하면 `USER_REVIEW.md`를 해소 상태로 갱신하고, `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. +- 새 구현이 필요하면 `plan` 스킬이 `USER_REVIEW.md`를 `user_review_N.log`로 아카이브한 뒤 새 `PLAN-*-G??.md` / `CODE_REVIEW-*-G??.md`를 작성한다. diff --git a/agent-ops/skills/common/commit-push/SKILL.md b/agent-ops/skills/common/commit-push/SKILL.md new file mode 100644 index 0000000..c2fad97 --- /dev/null +++ b/agent-ops/skills/common/commit-push/SKILL.md @@ -0,0 +1,162 @@ +--- +name: commit-push +version: 1.2.0 +description: 변경 사항을 커밋하거나 원격에 푸시한다. "커밋해줘", "푸시해줘", "커밋하고 푸시", "올려줘" 요청 시 사용하며 명시된 푸시는 재확인 없이 수행한다. +--- + +# commit-push + +## 목적 + +현재 변경 내용을 분석하여 Conventional Commits 형식의 한국어 설명 커밋 메시지를 작성하고, 사용자가 요청한 커밋 또는 푸시를 수행한다. +일반적인 커밋/푸시는 승인 질문 없이 진행하고, 실제 위험 신호가 있을 때만 사용자 확인을 받는다. + +## 언제 호출할지 + +- "커밋해줘", "커밋하고 푸시해줘" 요청 시 +- "변경 사항 올려줘", "푸시해줘" 요청 시 +- 코드 작업 완료 후 "반영해줘", "올려줘" 요청 시 + +## 입력 + +- `scope`: 커밋 범위 — all(전체) 또는 특정 파일/경로 (선택, 기본값: all) +- `push`: 푸시 여부 (선택, 기본값: auto) + - auto: 요청에 "푸시", "push", "올려줘", "반영해줘", "커밋하고 푸시"가 있으면 true, "커밋만" 또는 "푸시하지 마"가 있으면 false + - true: 푸시 여부를 다시 묻지 않고 현재 브랜치를 원격에 푸시한다 + - false: 커밋만 수행하고 푸시는 생략한다 + +## 먼저 확인할 것 + +- [ ] 커밋할 변경 사항 또는 push할 unpushed commit이 존재하는가 (`git status`로 확인) +- [ ] 현재 브랜치, 원격, upstream 상태가 일반 커밋/푸시에 적합한가 +- [ ] `.env`, 시크릿 파일 등 민감한 파일이 스테이징에 포함되어 있지 않은가 + +## 실행 절차 + +1. **변경 사항 파악** + - `git status`로 변경/추가/삭제된 파일 목록을 확인한다 + - `git diff`로 구체적인 변경 내용을 확인한다 + - 커밋할 변경 사항이 없고 push가 true이며 unpushed commit이 있으면 커밋 단계는 건너뛰고 푸시를 진행한다 + - 커밋할 변경 사항도 없고 push할 unpushed commit도 없으면 사용자에게 알리고 종료한다 + +2. **민감 파일 검사** + - 스테이징 대상에 아래 패턴이 포함되면 해당 파일을 제외하고 사용자에게 경고한다 + - `.env`, `.env.*` + - `*secret*`, `*credential*`, `*password*` + - `*.pem`, `*.key`, `*.p12` + - `.gitignore`에 이미 등록된 파일은 무시한다 + +3. **커밋 메시지 작성** + - 변경 내용을 분석하여 Conventional Commits 형식으로 커밋 메시지를 작성한다 + - 아래 메시지 규칙을 따른다 + +4. **위험 신호 판단** + - 아래 위험 신호가 없으면 사용자 승인 없이 다음 단계로 진행한다 + - 위험 신호가 있으면 커밋/푸시를 멈추고 대상 파일, 커밋 메시지, 위험 내용을 짧게 보여준 뒤 사용자 확인을 받는다 + - 민감 파일 패턴이 스테이징 대상에 포함됨 + - 사용자가 지정한 scope 밖의 변경을 포함해야 함 + - 현재 브랜치가 사용자 요청이나 작업 맥락과 다를 가능성이 큼 + - merge/rebase 충돌, detached HEAD, git 작업 중 상태가 감지됨 + - 원격이 diverged 상태거나 일반 push가 실패해 force push가 필요해 보임 + - 사용자가 메시지 수정을 요청하면 반영한다 + +5. **커밋 실행** + - 커밋할 변경 사항이 없고 push할 unpushed commit만 있으면 이 단계를 건너뛴다 + - scope에 따라 파일을 스테이징한다 + - all: 변경된 전체 파일을 개별적으로 `git add` + - 특정 경로: 해당 파일만 `git add` + - 작성한 메시지로 `git commit`을 실행한다 + +6. **푸시 실행** + - push가 true인 경우에만 실행한다 + - 사용자가 푸시를 명시했으면 푸시 여부를 다시 묻지 않는다 + - 현재 브랜치를 원격에 푸시한다 + - 원격 브랜치가 없으면 `-u origin {branch}`로 설정한다 + - 일반 push가 실패하거나 충돌이 발생하면 사용자에게 알리고 중단한다 + - `--force`가 필요해 보여도 자동 실행하지 않고 사용자 확인을 받는다 + +7. **결과 보고** + - 커밋 해시, 메시지, 변경 파일 수, 푸시 결과를 출력한다 + +## 커밋 메시지 규칙 + +### 형식 + +``` +(): <한국어 서술형 요약> + +<왜 변경했는지 설명하는 한국어 본문> (선택) + +