commit c05b4226bfa3fed177a4cd4a6a2f086a79423ff0 Author: gitea-spec-agent Date: Tue Sep 29 06:59:17 2026 +0000 Initialize gitea-agent harness diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a363f7c --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Runtime repository mirrors and worktrees +/repositories/ + +# Harness-generated runtime and review artifacts +/harness/.review-runs/ +/harness/.runtime/ + +# Python environments and caches +**/.venv/ +**/__pycache__/ +**/.pytest_cache/ +**/.ruff_cache/ +**/.mypy_cache/ +*.py[cod] + +# Local credentials and environment overrides +.env +.env.* +!.env.example +*.pem +*.key + +# Logs and local OS/editor files +*.log +.DS_Store +.vscode/ +.idea/ diff --git a/harness/agents/impl/skills/gitea-issue-implementation/SKILL.md b/harness/agents/impl/skills/gitea-issue-implementation/SKILL.md new file mode 100644 index 0000000..267077c --- /dev/null +++ b/harness/agents/impl/skills/gitea-issue-implementation/SKILL.md @@ -0,0 +1,230 @@ +--- +name: gitea-issue-implementation +description: 승인된 Gitea Issue를 구현·검증하고 Gitea PR로 전달한다. +version: 0.2.0 +author: genichin, Hermes Agent +license: MIT +platforms: [linux] +metadata: + hermes: + tags: [gitea, implementation, worktree, testing, pull-request] + related_skills: [] +--- + +# Gitea Issue Implementation + +승인된 Gitea Discovery와 pinned repository revision을 근거로 Issue별 persistent worktree에서 요구 문서, source와 test를 구현하고 검증한 뒤 Issue 전용 branch와 Gitea PR로 전달한다. Repository cache와 worktree 규칙은 [Repository cache 운영 지침](../../../../operations/repository-cache.md), 문서 형식은 `docs/` 아래 각 문서 유형의 README와 TEMPLATE만 정본으로 사용한다. Exact Issue 구현 요청은 승인된 범위의 commit, 일반 push와 PR 생성을 승인하지만 merge, force-push, Issue close, release 또는 deployment를 승인하지 않는다. + +## 사용 시점 + +다음과 같이 exact repository와 Issue를 지정해 구현을 요청받았을 때 사용한다. + +```text +/ 이슈 #를 구현하라 +``` + +이 요청은 현재 authoritative Issue body를 구현 대상으로 승인하고 다음 작업을 허용한다. + +- canonical Issue worktree 생성 또는 유효한 기존 worktree 재사용 +- 승인된 범위의 요구 문서, source, test와 repository 설정 수정 +- synthetic fixture와 test-only credential을 사용한 build, test, lint와 정적 검증 +- 구현 결과와 검증 evidence의 local 보존 +- 승인된 diff만 포함하는 local commit 생성 +- Issue 전용 remote branch의 신규 생성과 non-force push +- Target repository write 권한이 없을 때 authenticated identity 소유 fork 생성·재사용과 cross-repository PR 전달 +- default branch를 base로 하는 Gitea PR 생성과 생성 결과 readback +- PR 번호 확정 후 요구 문서의 `구현 PR: 미정`을 실제 번호 또는 URL로 갱신하는 후속 commit과 non-force push + +다음 작업은 요청 문구에 별도로 명시되지 않으면 승인되지 않는다. + +- force-push, rebase, amend 또는 다른 history rewrite +- default branch나 기존 비전용 remote branch로의 push +- PR merge, close, review, reviewer 지정 또는 승인 범위를 바꾸는 수정 +- Issue comment, body, title, state, label, assignee 또는 milestone 변경 +- release, package 또는 deployment 게시 + +Discovery 작성·검토, 미해결 요구사항 선택, 단순 repository 조사 또는 이미 구현된 변경의 독립 PR review에는 사용하지 않는다. + +## 정본과 승인 gate + +구현을 시작하려면 현재 Issue body가 다음을 모두 충족해야 한다. + +- 첫 줄이 exact `` marker다. +- 요약, 문제, 포함 범위, 제외 범위, 제약 조건, 확정된 결정, 저장소 근거, 문서 영향 및 산출물, 미해결 질문을 판독할 수 있다. +- 미해결 질문이 exact `없음`이거나 동등하게 미해결 결정이 없음을 명확히 나타낸다. +- pinned source revision이 full commit SHA로 하나만 확정돼 있다. +- 구현 산출물의 exact 경로와 요구사항 ID를 안전하게 결정할 수 있다. +- 서로 충돌하는 결정, 선택되지 않은 선택지, placeholder 또는 실제 credential이 없다. + +정확한 구현 요청은 해당 시점의 유효한 current body에 대한 구현 승인이다. 그러나 미해결 질문, malformed Discovery, 결정 충돌, 불명확한 범위 또는 credential 노출이 있으면 사용자의 의도를 추측하지 않고 worktree나 repository file을 변경하지 않는다. + +Issue body와 `updated_at`을 immutable approval snapshot으로 보존한다. body의 exact UTF-8 SHA-256 digest를 `terminal`로 계산하며 식별자나 digest를 임의로 정규화하지 않는다. 구현 중 authoritative Issue가 snapshot과 달라지면 새 내용을 자동 채택하지 않고 drift로 중단한다. + +## Lock과 권한 경계 + +모든 cache, lock, path, credential, worktree 생성·재사용·제거 규칙은 작업할 때마다 `read_file`로 `operations/repository-cache.md`를 새로 읽어 적용한다. 이 스킬의 요약이 canonical 문서를 완화하거나 대체하지 않는다. + +- Issue workflow 전체에서 exact `issue-.lock`을 유지한다. +- Cache 초기화·fetch·ref 검증과 worktree 등록 변경에는 canonical 순서로 repository lock을 추가 획득한다. +- 일반 편집과 test에는 Issue lock만 유지한다. +- 다른 Issue worktree나 다른 checkout을 읽기·수정·정리하지 않는다. +- lock을 획득할 수 없거나 현재 실행이 lock 순서를 증명할 수 없으면 fail-closed한다. +- 종료, 오류와 중단 경로에서 현재 실행이 획득한 lock만 역순으로 해제한다. + +## 절차 + +### 1. Target과 identity 고정 + +1. 요청에서 exact `owner`, `repository`와 Issue 번호를 추출하고 각각 canonical identifier 규칙으로 검증한다. +2. Issue lock을 획득한다. +3. Gitea MCP `get_me`로 현재 impl role identity를 확인한다. +4. Gitea MCP로 exact repository metadata, default branch와 SHA, Issue 전체 필드와 전체 comment baseline을 읽는다. +5. title, body, state, labels, assignees, milestone, `updated_at`, comment cardinality와 body digest를 immutable approval snapshot으로 기록한다. + +완료 조건: authenticated identity, exact target과 재현 가능한 approval snapshot이 모두 존재한다. + +### 2. Discovery와 산출물 계약 추출 + +1. 승인 gate로 current body를 검증한다. +2. 포함 범위, 제외 범위, 제약 조건과 확정된 결정을 중복 없이 추출한다. +3. 각 문서의 조치, exact path와 필수 내용을 추출한다. `영향 없음`인 문서를 생성하지 않는다. +4. 사용자 관찰 가능 동작, 실패 동작, 보안 불변조건, 호환성 조건과 외부 write 경계를 acceptance matrix로 만든다. +5. 각 acceptance criterion에 예정된 문서, source 위치와 검증 test를 연결한다. 연결할 수 없는 criterion이 있으면 구현 전에 중단한다. + +완료 조건: 모든 확정된 결정과 산출물이 acceptance matrix의 한 항목 이상에 대응하고, 제외 범위가 구현 항목에 포함되지 않는다. + +### 3. Cache 검증과 base 결정 + +1. `operations/repository-cache.md`를 새로 읽는다. +2. Canonical 절차로 cache를 초기화하거나 기존 cache를 검증한다. malformed cache를 삭제, reset, chmod, chown 또는 다른 repository로 교체하지 않는다. +3. Issue lock 아래 repository lock을 획득해 exact origin에서 prune/fetch하고 Gitea default branch SHA와 local ref 일치를 검증한다. +4. Discovery pinned SHA가 cache에 존재하는 commit object이고 Issue가 주장한 revision과 exact-match하는지 확인한다. +5. Pinned SHA와 current default branch SHA가 같으면 이를 base로 확정한다. +6. 두 SHA가 다르면 pinned revision부터 current default branch까지의 commit과 변경 파일을 조사한다. 변경이 Discovery 근거, 요구 문서 ID·경로, 대상 API, test contract 또는 acceptance criteria를 무효화하지 않는다는 근거가 있을 때만 current default branch SHA를 base로 사용한다. +7. 변경이 material하거나 요구사항과 충돌하거나 안전하게 판정할 수 없으면 worktree를 만들거나 수정하지 않고 renewed Discovery가 필요하다고 보고한다. + +완료 조건: Gitea와 local에서 확인된 full base SHA, 선택 근거와 pinned-to-base 차이 판정이 기록돼 있다. + +### 4. Worktree 생성 또는 재사용 + +1. Approval snapshot이 아직 current Issue와 exact-match하는지 다시 읽는다. +2. `operations/repository-cache.md`의 admission gate를 그대로 적용한다. +3. 신규 worktree는 canonical path에서 exact base SHA의 detached HEAD로 생성하고 Issue 용도로 lock한다. +4. `gitea-agent.worktree/v1` state를 canonical 방식으로 게시한다. +5. 기존 worktree는 state identity, Git registration, common directory, detached·locked 상태, base ancestry, dirty 상태와 미완료 Git operation을 모두 검증한 뒤 재사용한다. +6. 기존 worktree의 파일, untracked state 또는 commit을 reset, clean, rebase, checkout, switch 또는 삭제로 자동 복구하지 않는다. +7. Repository lock을 해제하고 Issue lock은 유지한다. + +완료 조건: exact worktree path, base SHA, current HEAD, detached·locked 상태와 approval snapshot binding이 canonical 검증을 통과한다. + +### 5. Repository baseline 조사 + +1. `search_files`, `read_file`과 worktree를 대상으로 한 read-only `terminal` Git 명령으로 tree, build 설정, test runner, formatter, lint와 기존 문서 구조를 조사한다. +2. Discovery가 언급한 모든 source와 test 경로를 실제 base에서 읽는다. 읽지 않은 파일을 근거로 구현을 설계하지 않는다. +3. Repository가 선언한 canonical install과 test 명령을 찾는다. 임의의 전역 package 설치나 운영 credential을 사용하지 않는다. +4. 관련 기존 test를 수정 전 실행하고 exit code와 실패 내용을 보존한다. +5. Baseline 실패가 변경 대상과 관련되거나 새 구현의 판정을 방해하면 수정하지 않고 blocker로 보고한다. 명백히 무관한 기존 실패라면 범위와 evidence를 기록하고 영향받는 test를 별도로 판정한다. + +완료 조건: 변경 대상의 현재 동작, dependency direction, test command와 baseline 결과가 실제 output으로 확인됐다. + +### 6. 요구 문서 작성 + +Discovery가 `신규 작성` 또는 `수정`으로 판정한 문서만 작성한다. + +1. 요구사항명세서는 `read_file`로 harness의 [요구사항 작성 방법](../../../../docs/requirements/README.md)과 [요구사항 템플릿](../../../../docs/requirements/TEMPLATE.md)을 새로 읽고 Discovery가 지정한 target repository의 exact ID·경로로 작성한다. +2. 데이터 모델은 영향이 있을 때만 harness의 [데이터 모델 작성 방법](../../../../docs/data-model/README.md)과 [데이터 모델 템플릿](../../../../docs/data-model/TEMPLATE.md)을 적용한다. +3. 인터페이스 계약은 영향이 있을 때 harness의 [인터페이스 계약 작성 방법](../../../../docs/interface-contract/README.md)과 [인터페이스 계약 템플릿](../../../../docs/interface-contract/TEMPLATE.md)을 적용한다. +4. 프로젝트 구조는 영향이 있을 때 harness의 [프로젝트 구조 작성 방법](../../../../docs/project-structure/README.md)과 [프로젝트 구조 템플릿](../../../../docs/project-structure/TEMPLATE.md)을 적용한다. +5. 런타임 흐름은 영향이 있을 때 harness의 [런타임 흐름 작성 방법](../../../../docs/runtime-flow/README.md)과 [런타임 흐름 템플릿](../../../../docs/runtime-flow/TEMPLATE.md)을 적용한다. +6. Requirement frontmatter는 canonical `gitea-agent.requirement/v1` 계약을 만족하고 status를 `approved`로 기록한다. 구현 PR이 아직 없으면 `미정`을 유지하고 번호를 추측하지 않는다. +7. 모든 placeholder, 작성 안내 문구, 실제 credential과 확인되지 않은 경로를 제거한다. + +완료 조건: Discovery의 문서 영향 판정과 생성·수정된 문서 집합이 정확히 일치하고 각 문서가 해당 README와 TEMPLATE 검증을 통과한다. + +### 7. Test-first 구현 + +1. Acceptance matrix에서 변경 전 실패해야 하는 정상, 실패, 경계와 보안 test를 먼저 추가한다. +2. 새 test가 의도한 이유로 실패하는지 실행해 확인한다. 환경 오류나 잘못된 fixture 실패를 RED evidence로 사용하지 않는다. +3. 기존 architecture와 dependency direction을 유지하는 최소 source 변경으로 test를 통과시킨다. +4. 실패 경로가 외부 상태를 변경하지 않는지 검증한다. +5. 실제 운영 데이터, password, token 또는 credential을 test와 fixture에 사용하지 않는다. +6. Timing, concurrency 또는 retry 조건은 clock·sleep 의존성을 주입하거나 통제 가능한 seam으로 unit test하되, Discovery가 실제 elapsed-time 보장을 요구하면 실제 통합 경로 test도 유지한다. +7. 관련 test를 작은 단위로 반복한 뒤 전체 관련 suite를 실행한다. + +완료 조건: 각 acceptance criterion에 실제로 실행된 test 또는 판정 가능한 문서·정적 검증 evidence가 연결돼 있다. + +### 8. 통합 검증과 독립 검토 + +1. Repository가 요구하는 formatter, lint, type check, build와 test suite를 실제 실행한다. +2. Base-to-worktree diff를 읽고 모든 변경 파일을 acceptance matrix와 대응한다. +3. 범위 밖 변경, debug artifact, generated drift, credential material, query parameter secret, test-only bypass와 운영 데이터가 없는지 검사한다. +4. 새 dependency가 있다면 필요성과 lockfile·deployment 반영을 검증한다. +5. 설정 예시에는 secret 이름과 용도만 기록하고 실제 값을 넣지 않는다. +6. 독립 검토가 유용하면 `delegate_task`로 bounded reviewer를 사용한다. Reviewer에게 exact worktree path, base SHA, Issue snapshot, acceptance matrix와 검증 명령을 전달하고 Gitea write, Git cleanup, commit과 push를 금지한다. +7. Main agent가 reviewer 지적을 직접 재현하고 유효한 문제만 수정한 뒤 영향을 받은 검증을 다시 실행한다. + +완료 조건: 모든 modified file이 승인된 산출물로 설명되고, 실행한 quality gate와 unresolved finding이 명확하다. + +### 9. Drift 확인, commit과 PR 전달 + +1. Gitea Issue와 comments를 다시 읽어 approval snapshot의 body, `updated_at`과 comment baseline drift를 확인한다. +2. Repository lock 아래 origin을 다시 fetch하고 Gitea default branch SHA와 local ref를 확인한다. +3. Default branch가 base 이후 전진했으면 자동 merge, rebase 또는 history rewrite하지 않는다. 새 변경이 구현과 충돌하지 않는지 판정할 수 없으면 delivery-ready로 보고하지 않는다. +4. Worktree의 base SHA, final HEAD, staged·unstaged·untracked 상태, 미완료 Git operation과 test 결과를 기록한다. +5. Branch 이름은 repository convention이 있으면 이를 따르고, 없으면 `gitea-agent/issue--`처럼 deterministic Issue 전용 이름을 사용한다. Gitea와 remote에서 동일 이름의 branch와 열린 PR을 먼저 조회해 중복 생성하지 않는다. +6. 승인된 modified·untracked file을 exact allowlist로 stage하고 staged diff를 다시 읽는다. 범위 밖 파일, credential, generated artifact 또는 예상하지 못한 삭제가 있으면 commit하지 않는다. +7. Repository convention에 맞는 non-amended commit을 생성하고 base ancestry, commit tree와 worktree 상태를 검증한다. +8. Issue 전용 branch가 없으면 final commit을 가리키도록 생성하고, 있으면 현재 작업이 소유한 branch이며 fast-forward 가능한 경우에만 갱신한다. Default branch에는 직접 push하지 않고 force-push하지 않는다. Canonical cache의 mirror remote 설정을 변경하지 말고 push할 때 explicit repository URL과 refspec을 사용한다. +9. Target repository write 권한이 없으면 같은 push를 반복하지 않는다. Authenticated identity가 fork를 생성할 수 있으면 기존 fork·동명 branch·PR 유무와 fork parent/base SHA를 검증한 뒤 fork의 Issue 전용 branch로 non-force push하고 cross-repository PR을 사용한다. Fork도 쓸 수 없으면 permission blocker와 local commit SHA를 보고한다. +10. Remote readback으로 branch owner, repository, branch SHA가 local commit SHA와 exact-match하는지 확인한다. +11. Default branch를 base로 PR을 생성한다. 제목은 Issue를 식별하고, 본문에는 `Closes #`, 구현 요약, 문서 변경, 실제 검증 명령·결과, 제한사항과 보안상 주의점을 기록한다. PR 생성 자체로 Issue state를 변경하지 않는다. +12. PR 번호를 얻은 뒤 요구사항 문서의 `구현 PR: 미정`을 실제 PR 번호 또는 URL로 교체하고, 문서 검증과 관련 test를 재실행해 별도 commit으로 같은 branch에 non-force push한다. +13. Gitea에서 PR details, base branch, head repository·branch·SHA, state, changed files와 commit을 다시 읽는다. 최종 remote head SHA가 최종 local commit SHA와 일치할 때만 전달 완료로 판정한다. +14. PR 생성 또는 후속 push가 부분 실패하면 blind retry하거나 중복 PR을 만들지 않는다. 이미 생성된 fork·branch·PR과 local commit의 exact 상태 및 안전한 복구 지점을 보고한다. + +완료 조건: Issue와 source drift, final worktree 상태와 실행 결과가 fresh readback으로 확인되고, 열린 Gitea PR의 base·head와 remote head SHA가 검증된 최종 local commit에 exact-match한다. + +## 위임 규칙 + +Main agent는 구현 범위와 최종 결과에 책임을 진다. 독립적인 repository 조사, test 작성, 특정 모듈 구현, 보안 검토 또는 실패 분석은 병렬 위임할 수 있지만 다음을 지킨다. + +- Delegate에 Issue body 전체가 아니라 필요한 확정 계약, exact worktree path와 금지된 side effect를 명시한다. +- 같은 파일을 병렬로 수정하는 worker를 만들지 않는다. +- Delegate가 Gitea write, lock 변경, worktree 생성·제거, commit, push 또는 reset을 수행하지 못하게 한다. +- Delegate의 성공 주장을 evidence로 간주하지 않고 main agent가 diff와 test output을 직접 확인한다. +- 외부 side effect는 main agent만 수행하고 즉시 authoritative readback한다. + +## 주의점과 금지 사항 + +- 미해결 질문이나 충돌하는 결정을 임의로 선택하지 않는다. +- Issue body에 없는 기능, migration, refactor 또는 dependency를 편의상 추가하지 않는다. +- Moving branch나 stale cache를 구현 근거로 사용하지 않는다. +- Bare mirror에 구현용 local branch를 만들지 않는다. +- 다른 worktree의 변경을 가져오거나 정리하지 않는다. +- 기존 dirty state를 reset, clean, stash, checkout 또는 force remove하지 않는다. +- test를 통과시키기 위해 production security gate, validation 또는 실패 경로를 약화하지 않는다. +- 실제 credential, 운영 DB 또는 사용자 금융 데이터를 test나 artifact에 사용하지 않는다. +- 실행하지 않은 test, 읽지 않은 파일 또는 확인하지 않은 Gitea 상태를 성공 evidence로 보고하지 않는다. +- Issue 구현 요청 범위를 벗어난 commit·push·PR 변경, force-push, merge, Issue 변경, release 또는 deployment를 수행하지 않는다. +- 실패하거나 불확실한 외부 write를 blind retry하지 않는다. + +## 검증 및 보고 + +완료 보고에는 다음을 구분해 포함한다. + +- authenticated Gitea identity와 exact target +- approval snapshot의 Issue `updated_at`, body digest와 미해결 질문 검증 +- cache path, bare·remote 검증, fetch 결과와 Gitea/local SHA +- Discovery pinned SHA, selected base SHA와 차이 판정 +- worktree path, 신규 생성 또는 재사용, detached·locked 상태와 final HEAD +- 요구 문서별 조치, exact path와 canonical template 검증 +- acceptance criterion별 구현 위치와 test evidence +- baseline, targeted와 full-suite 명령의 실제 exit 결과 +- modified file 전체와 범위 내 변경 근거 +- staged, unstaged, untracked, 미완료 Git operation, local commit과 remote branch SHA +- Issue와 default branch의 최종 drift readback +- PR 번호·URL·base·head·state와 PR head SHA readback +- 수행한 commit·push·PR write와 수행하지 않은 merge·Issue·release write +- blocker, 기존 실패, unresolved risk 또는 partial 상태 + +파일 작성, process exit, test 일부 통과, local commit 또는 delegate 보고만으로 완료를 주장하지 않는다. 모든 승인된 산출물이 worktree에 존재하고 acceptance matrix, 실제 test output, final diff, remote branch와 열린 PR의 fresh Gitea readback이 일치할 때만 구현 완료로 판정한다. diff --git a/harness/agents/release/skills/gitea-release/SKILL.md b/harness/agents/release/skills/gitea-release/SKILL.md new file mode 100644 index 0000000..a9a9b18 --- /dev/null +++ b/harness/agents/release/skills/gitea-release/SKILL.md @@ -0,0 +1,207 @@ +--- +name: gitea-release +description: 승인된 Gitea 버전을 준비·태깅하고 산출물을 게시한다. +version: 0.1.0 +author: genichin, Hermes Agent +license: MIT +platforms: [linux] +metadata: + hermes: + tags: [gitea, release, worktree, tag, artifact] + related_skills: [gitea-repository-cache-bootstrap] +--- + +# Gitea Release + +Gitea repository cache를 사용해 버전 준비, 검증, 태깅과 프로젝트별 산출물 게시를 수행한다. Cache 규칙은 [Repository cache 운영 지침](../../../../operations/repository-cache.md)을 정본으로 사용한다. + +## 사용 시점 + +- exact `owner/repository`의 새 버전 준비를 요청받았을 때 +- 준비된 commit을 태깅하고 승인된 산출물을 게시하도록 요청받았을 때 +- 기존 릴리즈의 tag, commit과 게시 산출물을 검증할 때 + +Issue 구현, PR merge, 임의 버전 선택, 기존 tag 이동, artifact 덮어쓰기 또는 deployment에는 사용하지 않는다. + +## 책임 분리 + +### 공통 릴리즈 처리 + +이 스킬이 담당한다. + +- repository cache 검증과 fetch +- release lock과 worktree 관리 +- 버전 변경 파일 제한 +- 간단한 준비 검증 +- local commit 생성과 commit tree 검증 +- 승인된 commit에 tag 생성·push +- 프로젝트별 게시 절차 호출 +- 원격 tag와 artifact readback +- 성공한 worktree 정리 + +### 프로젝트별 처리 + +Target repository의 release contract가 담당한다. + +- version source와 함께 변경할 lockfile +- test, lint와 build 명령 +- artifact 종류와 이름 +- 대상 platform과 build context +- registry, package namespace와 인증 secret 이름 +- smoke test와 게시 명령 +- digest, checksum 또는 provenance 검증 + +Release contract가 없거나 모호하면 기존 workflow, build metadata와 문서를 읽어 확인된 사실만 보고한다. 이미지 이름, registry, credential, 명령 또는 artifact를 추측하지 않는다. + +## 승인 계약 + +### 준비 승인 + +버전 변경과 local commit을 시작하려면 다음이 필요하다. + +- exact `owner/repository` +- exact version과 tag +- 버전 파일 변경에 대한 명시적 승인 + +Agent는 버전을 선택, 증가, 정규화 또는 교체하지 않는다. + +### 게시 승인 + +Tag 또는 artifact를 외부에 게시하려면 다음이 필요하다. + +- exact `owner/repository` +- exact version과 tag +- 준비된 full commit SHA +- exact artifact와 게시 대상 + +브랜치명, 축약 SHA, `HEAD`, moving tag 또는 unstated default는 게시 승인이 아니다. + +## Release worktree 모델 + +Repository마다 동시에 하나의 release workflow만 허용한다. + +```text +/opt/data/gitea-agent/repositories/// +├── locks/release.lock +└── worktrees/release/ + ├── state.json + └── checkout/ +``` + +- `release.lock`을 전체 workflow 동안 유지한다. +- Cache fetch, worktree 등록 변경과 제거에는 `release.lock → repository.lock` 순서로 획득한다. +- Worktree는 확인된 원격 default-branch full SHA의 detached HEAD로 생성하고 lock한다. +- 기존 release worktree를 reset, clean, rebase, checkout, force-remove 또는 덮어쓰지 않는다. +- 실패한 release worktree는 증거 보존을 위해 자동 제거하지 않는다. + +`state.json`에는 최소한 schema `gitea-agent.release-worktree/v1`, repository identity, base SHA, requested version·tag, prepared commit SHA와 단계 상태를 기록한다. Credential, PID 또는 secret 값은 기록하지 않는다. + +## 절차 + +### 1. Target과 cache 확인 + +1. 요청의 repository, version, tag와 승인 범위를 추출한다. +2. Gitea MCP로 authenticated identity, repository metadata, default branch와 SHA를 읽는다. +3. `operations/repository-cache.md`에 따라 cache를 검증하거나 초기화한다. +4. Release lock 아래 repository lock을 획득해 exact origin을 prune/fetch한다. +5. Gitea default-branch SHA와 local mirror ref 일치를 확인한다. + +완료 조건: authoritative repository identity와 local cache가 같은 full SHA를 가리킨다. + +### 2. Release worktree 생성 + +1. 기존 release worktree, Git registration과 state를 함께 조사한다. +2. 대상이 모두 없을 때만 exact default-branch SHA에서 detached worktree를 만든다. +3. Worktree를 release 용도로 lock하고 `state.json`을 원자적으로 기록한다. +4. HEAD, detached 상태, common Git directory와 clean 상태를 다시 확인한다. + +완료 조건: release worktree가 확인된 base SHA에서 clean·detached·locked 상태다. + +### 3. 프로젝트 계약 조사 + +1. `search_files`와 `read_file`로 version metadata, lockfile, test, build, 기존 workflow와 release 문서를 읽는다. +2. Version 변경 파일, 준비 검증과 게시 adapter를 구분한다. +3. 정확한 게시 대상이나 credential 가용성을 확인할 수 없으면 수정 전에 중단한다. + +완료 조건: 변경 파일 allowlist, 준비 검증 명령과 프로젝트별 게시 절차가 명확하다. + +### 4. 버전 변경과 준비 commit + +1. 승인된 exact version만 allowlist 파일에 반영한다. +2. Project-defined 준비 test와 정적 검사를 실행한다. +3. 수정·미추적 파일 전체를 검사하고 allowlist 밖의 변경이 있으면 중단한다. +4. Exact path만 stage하고 staged diff, secret, 삭제와 whitespace 오류를 검토한다. +5. Non-amended local commit을 생성한다. +6. Commit의 parent, tree, version·tag 일치와 clean worktree를 검증한다. +7. `state.json`에 prepared full SHA와 검증 결과를 기록한다. + +완료 조건: 승인된 버전 변경만 포함한 하나의 검증된 local commit이 존재한다. + +### 5. 게시 gate + +다음을 모두 확인한다. + +- 게시 승인이 exact prepared full SHA를 포함한다. +- Gitea와 local에서 동일 tag가 존재하지 않는다. +- 동일 artifact version 또는 image tag가 존재하지 않는다. +- 필요한 credential과 target이 확인됐다. +- 필수 test와 check가 exact prepared commit에서 성공했다. +- Worktree HEAD와 tree가 준비 검증 이후 바뀌지 않았다. + +하나라도 불명확하면 외부 상태를 변경하지 않는다. + +### 6. Tag와 프로젝트별 게시 + +1. 승인된 exact tag를 prepared commit에 생성한다. +2. Explicit remote URL과 refspec으로 신규 tag만 non-force push한다. +3. Gitea readback으로 remote tag target이 prepared SHA와 일치하는지 확인한다. +4. Project release contract의 build, smoke test와 publish 절차를 그대로 실행한다. +5. 승인된 artifact와 immutable commit-derived tag만 게시한다. + +`latest`, 추가 channel, 추정 artifact 또는 기존 tag를 게시·이동·덮어쓰지 않는다. + +### 7. 권위 있는 검증 + +- Gitea remote tag target을 다시 읽는다. +- 게시 artifact의 version, digest 또는 checksum을 authoritative target에서 읽는다. +- Artifact provenance가 prepared SHA와 일치하는지 확인한다. +- 여러 tag를 게시했다면 모두 같은 승인 artifact를 가리키는지 확인한다. +- Gitea Release가 별도 승인됐다면 target, metadata와 attachments를 다시 읽는다. + +완료 조건: remote tag와 모든 승인 산출물이 prepared SHA에 연결되고 immutable identifier로 확인된다. + +### 8. 정리 + +모든 readback이 성공하고 worktree가 clean하며 prepared commit이 remote tag로 보존된 경우에만 release worktree를 제거한다. + +1. Release lock과 repository lock을 유지한다. +2. Worktree lock을 해제하고 Git operation으로 registration과 checkout을 제거한다. +3. `state.json`과 빈 release directory만 제거한다. +4. Worktree와 registration 부재를 확인한 뒤 lock을 역순으로 해제한다. + +Partial failure, dirty state, 미게시 commit 또는 불확실한 readback에서는 정리하지 않는다. + +## 실패 처리 + +- Version, tag, commit, artifact 또는 target을 추측하지 않는다. +- 기존 tag를 force-update, 삭제 또는 재생성하지 않는다. +- 기존 artifact를 덮어쓰거나 삭제하지 않는다. +- 실패한 build를 임의 변경으로 수리하지 않는다. +- External write 실패를 blind retry하지 않는다. +- 부분 성공 상태와 안전한 재개 지점을 보존하고 사용자 결정을 요청한다. + +## 검증 및 보고 + +완료 보고에는 다음을 포함한다. + +- repository와 authenticated identity +- base, prepared와 remote tag full SHA +- exact version과 tag +- 변경 파일과 commit +- 실행한 test·build·smoke 명령의 실제 결과 +- 게시 artifact의 exact 이름과 immutable digest 또는 checksum +- 수행한 외부 write와 authoritative readback +- worktree 정리 또는 보존 상태 +- blocker와 partial failure + +명령 성공만으로 릴리즈 완료를 주장하지 않는다. Remote tag와 모든 승인 산출물의 fresh readback이 일치할 때만 완료다. diff --git a/harness/agents/spec/skills/gitea-discovery-authoring/SKILL.md b/harness/agents/spec/skills/gitea-discovery-authoring/SKILL.md new file mode 100644 index 0000000..78275b9 --- /dev/null +++ b/harness/agents/spec/skills/gitea-discovery-authoring/SKILL.md @@ -0,0 +1,106 @@ +--- +name: gitea-discovery-authoring +description: Gitea Issue의 Discovery 초안을 작성하고 게시한다. +version: 0.3.4 +author: genichin, Hermes Agent +license: MIT +platforms: [linux] +metadata: + hermes: + tags: [gitea, discovery, specification, authoring] + related_skills: [gitea-repository-cache-bootstrap] +--- + +# Gitea Discovery Authoring + +Gitea Issue 원본과 저장소 근거를 조사해 Discovery 초안을 작성하고 Issue 본문에 게시한다. 작성 규칙은 [Discovery 작성 방법](../../../../docs/discovery/README.md), 문서 구조는 [Discovery 템플릿](../../../../docs/discovery/TEMPLATE.md)만 정본으로 사용하며 이 스킬에 복제하거나 임의로 확장하지 않는다. + +## 사용 시점 + +다음과 같이 정확한 저장소와 Issue를 지정해 Discovery 초안 작성을 요청할 때 사용한다. + +```text +/의 이슈 #에 대해 discovery 초안을 작성하라 +``` + +이 요청은 원본 본문의 comment 보존과 Discovery 초안으로의 본문 교체를 승인한다. 다른 comment, label, assignee, title, state, milestone 또는 repository write는 승인하지 않는다. + +요구사항 문서, 구현 계획 또는 PR review 작성에는 사용하지 않는다. + +## Repository cache admission gate + +Repository cache의 구조와 운영 규칙은 [Repository cache 운영 지침](../../../../operations/repository-cache.md)만 정본으로 사용한다. 작업할 때마다 이 문서를 새로 읽고 상세 규칙을 이 스킬에 복제하거나 완화하지 않는다. + +Discovery 초안을 작성하기 전에 repository cache admission gate를 반드시 통과한다. 대상 cache가 없거나 초기화되지 않았다면 `gitea-repository-cache-bootstrap` 스킬로 초기화한다. 초기화 여부와 관계없이 canonical origin에서 cache를 갱신하고, 갱신 후 다시 읽은 Gitea authoritative default-branch SHA와 local ref SHA가 정확히 일치해야 한다. 일치한 SHA를 pinned source revision으로 고정하며, gate가 실패하면 초안을 작성하지 않는다. + +## 절차 + +1. 요청에서 exact `owner`, `repository`, Issue 번호를 추출하고 형식을 검증한다. +2. Issue lock을 획득한 상태에서 Gitea MCP `get_me`로 spec-agent identity를 확인한다. +3. Gitea MCP로 exact repository metadata, Issue 전체 내용과 전체 comment baseline을 읽는다. Issue title, body, state, labels, assignees, milestone과 `updated_at`을 immutable pre-write snapshot으로 보존한다. +4. `operations/repository-cache.md`를 새로 읽는다. Issue lock을 유지한 채 대상 cache가 없거나 초기화되지 않았다면 `gitea-repository-cache-bootstrap` 스킬을 사용한다. +5. bootstrap 완료 여부와 관계없이 canonical 순서에 따라 repository lock을 획득한다. path, bare 상태와 remote identity를 검증한 뒤 authoritative origin에서 prune/fetch한다. fetch 후 Gitea default branch와 SHA를 다시 읽고 local ref가 exact SHA와 일치하는지 확인하여 pinned source revision으로 기록한다. 어느 단계든 실패하면 repository lock을 해제하고 외부 write와 Discovery 작성을 수행하지 않는다. +6. `read_file`로 `docs/discovery/README.md`와 `docs/discovery/TEMPLATE.md`를 새로 읽는다. +7. repository lock을 유지한 채 Issue 원본과 pinned repository SHA의 tree 및 파일을 조사한다. 움직이는 branch 이름 대신 exact pinned SHA를 사용하고, `git --git-dir=/repository.git`의 read-only 명령 또는 이에 상응하는 안전한 reader만 사용하며 조사한 경로와 SHA를 기록한다. README가 정한 후속 문서의 exact 경로 존재 여부와 기존 요구사항명세서의 ID·파일명을 함께 조사한다. +8. 조사 종료 시 Gitea default-branch SHA와 local ref를 다시 읽어 pinned SHA와 모두 일치하는지 확인하고 repository lock을 해제한다. Issue lock은 전체 Issue workflow가 끝날 때까지 유지한다. +9. README의 작성 규칙과 TEMPLATE의 visible 구조를 그대로 적용해 Discovery body artifact를 작성한다. `문서 영향 및 산출물`은 README가 정한 모든 문서 유형을 빠짐없이 판정하고 문서별 필수 내용을 구현 에이전트가 추가 설계 없이 작성할 수 있을 정도로 구체화한다. 요구사항명세서는 pinned SHA에서 충돌 여부를 확인한 exact 문서 ID·경로·제목·포함할 내용·제외할 내용을 기록한다. 모든 `${...}` placeholder를 교체하고 TEMPLATE에 없는 hidden state, marker 또는 metadata를 추가하지 않는다. +10. body artifact의 첫 marker, 섹션 순서·제목, placeholder 부재, `문서 영향 및 산출물`의 문서 유형·순서·조치·경로·변경 내용·근거와 문서별 필수 내용 및 모든 저장소 근거가 pinned SHA에서 확인됐는지 검증한다. 신규 요구사항명세서의 경로에 `NNN`, `` 또는 다른 미확정 값이 남아 있거나 ID 충돌·형식 오류를 해소할 수 없으면 외부 write 없이 중단한다. +11. write 직전에 Issue와 comments를 다시 읽는다. pre-write snapshot의 body와 `updated_at`이 달라졌거나 comment baseline이 drift했으면 중단한다. +12. 아래의 단순 원본 보존 comment 형식으로 exact comment artifact를 만들고 한 번 작성한 뒤, 곧바로 comment 목록에서 authoritative readback한다. +13. comment readback 직후 Issue와 comments를 다시 읽어 post-comment snapshot을 만든다. body와 비본문 필드가 pre-write snapshot과 같고 comments가 baseline과 exact 신규 comment 하나로만 구성돼야 한다. 다르면 body를 변경하지 않고 partial 상태로 중단한다. +14. 보존 comment가 정확히 확인된 경우에만 Issue body 하나만 Discovery 초안으로 교체한다. provider가 조건부 update를 지원하면 post-comment `updated_at` 또는 ETag를 precondition으로 사용한다. 지원하지 않으면 Issue lock 아래에서 13번 read 직후 write하며 title, state, labels, assignees, milestone 등 다른 필드는 write payload에 포함하지 않는다. +15. 동일 Issue를 다시 읽어 body가 초안과 정확히 일치하고 다른 snapshot 필드가 변경되지 않았는지 확인한다. comments도 다시 읽어 보존 comment가 정확히 하나인지 확인한다. + +## 원본 보존 comment + +comment는 다음 형식을 사용한다. ``에는 write 직전 Gitea에서 읽은 원본 body를 변형 없이 넣는다. + +```text + +## Discovery 작성 전 이슈 원본 + + +``` + +`body-sha256`은 Gitea가 반환한 원본 body의 UTF-8 bytes에 대해 `terminal`로 계산한다. 게시 후 comment의 author, target marker, body digest와 `` exact bytes를 모두 검증한다. 이 comment metadata를 Issue body에 포함하지 않는다. + +- 원본 comment와 Discovery body는 trusted workflow directory에 artifact로 먼저 작성하고 실제 write payload와 exact-match하는지 확인한다. +- Discovery body는 TEMPLATE에서 만든 artifact의 exact bytes를 전송하고 즉시 Gitea MCP readback으로 검증한다. +- 다른 checkout, workspace, installed package 또는 외부 renderer를 import하지 않는다. +- credential 값은 출력하거나 artifact에 기록하지 않는다. + +## 재실행과 부분 실패 + +- 동일 target에 유효한 원본 보존 comment가 이미 있고 현재 body가 유효한 Discovery 초안이면 새 comment나 body write 없이 완료 상태를 readback한다. +- comment write 응답이 timeout 또는 오류이면 재시도하지 않는다. comment 목록을 한 번 조정해 baseline 대비 exact 신규 comment 하나만 확인될 때만 계속한다. +- 원본 comment는 게시됐지만 Issue body가 아직 원본과 같다면 partial 상태로 간주한다. exact marker, digest, author와 원본 일치를 검증한 뒤 기존 comment를 재사용하여 body 교체만 재개한다. +- 원본 comment가 여러 개이거나 marker/digest가 잘못됐거나 현재 body가 원본 및 유효한 Discovery 어느 쪽과도 일치하지 않으면 자동 복구하지 않는다. +- Issue body update가 불확실하면 다시 update하지 않고 exact Issue를 한 번 readback해 결과를 판정한다. + +## 금지 사항 + +- 현재 README와 TEMPLATE의 규칙을 이 스킬에서 재정의하지 않는다. +- repository cache의 dirty, malformed 또는 drift 상태를 자동 삭제·초기화·덮어쓰기하지 않는다. +- stale하거나 Gitea authoritative SHA와 일치하지 않는 cache를 Discovery 근거로 사용하지 않는다. +- 읽지 않은 파일이나 실행하지 않은 검증을 저장소 근거로 제시하지 않는다. +- 추천안을 사용자의 확정된 결정으로 표현하지 않는다. +- credential, token 또는 환경변수 값을 출력하거나 산출물에 포함하지 않는다. +- 원본 보존 comment 확인 전에 Issue body를 교체하지 않는다. +- 성공 여부가 불확실한 외부 write를 blind retry하지 않는다. + +## 검증 및 보고 + +완료 전에 다음을 모두 확인하고 구분해 보고한다. + +- authenticated MCP identity +- repository cache 경로, 신규 초기화 또는 기존 재사용 여부, fetch 결과, bare 상태, pinned branch와 Gitea/local SHA 일치 +- 원본 Issue snapshot과 comment baseline +- 조사한 repository evidence +- Discovery 문서 정본 검증 결과 +- `문서 영향 및 산출물`의 문서별 조치와 검증 결과 +- 원본 보존 comment ID, author, marker와 digest readback +- Issue body exact readback과 비본문 필드 불변성 +- 최종 comment cardinality와 중복 원본 comment 부재 +- dry-run/local 검증과 실제 Gitea write 결과 + +프로세스 exit나 MCP write 응답만으로 성공을 주장하지 않는다. authoritative readback이 하나라도 실패하면 partial 또는 failure로 보고한다. diff --git a/harness/agents/spec/skills/gitea-discovery-review/SKILL.md b/harness/agents/spec/skills/gitea-discovery-review/SKILL.md new file mode 100644 index 0000000..8631737 --- /dev/null +++ b/harness/agents/spec/skills/gitea-discovery-review/SKILL.md @@ -0,0 +1,95 @@ +--- +name: gitea-discovery-review +description: 수정된 Gitea Discovery를 검토하고 정규화한다. +version: 0.2.0 +author: genichin, Hermes Agent +license: MIT +platforms: [linux] +metadata: + hermes: + tags: [gitea, discovery, review, specification] + related_skills: [gitea-discovery-authoring, gitea-repository-cache-bootstrap] +--- + +# Gitea Discovery Review + +사용자가 Gitea Issue의 Discovery body에서 OQ를 선택하거나 본문을 수정한 뒤 그 수정본을 검토해 달라고 요청할 때 사용한다. 현재 Issue body를 review candidate의 정본으로 삼아 결정 전파, 모순 제거, 후속 OQ와 문서 영향 재평가를 수행한다. Discovery 승인, label·assignee 변경, 구현 handoff 또는 후속 문서·source 작성은 수행하지 않는다. + +작성 규칙은 [Discovery 작성 방법](../../../../docs/discovery/README.md), visible 구조는 [Discovery 템플릿](../../../../docs/discovery/TEMPLATE.md), cache와 lock 규칙은 [Repository cache 운영 지침](../../../../operations/repository-cache.md)만 정본으로 사용한다. + +## 사용 시점 + +다음과 같이 exact repository와 Issue의 수정된 Discovery 검토를 요청받았을 때 사용한다. + +```text +/ 이슈 #의 수정된 discovery를 검토하라 +``` + +이 요청은 실제 정규화가 필요할 때 Issue body만 교체하는 것을 승인한다. 검토 전 candidate를 comment로 보존하지 않으며 comment, title, state, label, assignee, milestone, repository file 또는 source write는 승인하지 않는다. 최초 Discovery 작성에는 `gitea-discovery-authoring`을 사용한다. + +## 입력 승인 gate + +- body 첫 줄이 exact ``이고 TEMPLATE의 H2 순서가 정확해야 한다. +- 최초 Discovery authoring의 원본 provenance comment는 authoring 계약에 맞는 exact comment 하나여야 하며 review는 이를 읽기만 한다. +- OQ ID는 고유하고 증가해야 하며 각 선택지는 checkbox여야 한다. +- OQ마다 선택된 항목이 0개 또는 1개여야 한다. 2개 이상이면 사용자의 결정을 추측하지 않고 외부 write 없이 중단한다. +- credential, token, password 또는 secret의 실제 값이 body에 포함된 것으로 의심되면 comment를 포함한 어떤 외부 write도 수행하지 않는다. +- malformed marker·섹션·comment·OQ 또는 identity가 불명확하면 자동 복구하지 않는다. + +## OQ 검토 규칙 + +- 선택이 0개인 OQ는 미해결 상태로 유지한다. +- 선택이 정확히 1개인 OQ는 선택지의 의미를 확대하지 않고 사용자 결정으로 채택한다. +- 채택한 결정은 `확정된 결정`에 반영하고 필요한 `포함 범위`, `제외 범위`, `제약 조건`, `저장소 근거`, `문서 영향 및 산출물`에 일관되게 전파한 뒤 해당 OQ를 제거한다. +- 선택된 결정이 기존 결정·범위·저장소 근거와 충돌하면 임의로 우선순위를 정하지 않는다. 충돌을 해소할 수 있는 새 OQ를 열거나, 선택지만으로 안전한 질문을 구성할 수 없으면 외부 write 없이 중단한다. +- 사용자 prose 수정은 현재 문서 전체와 pinned source revision을 기준으로 검토한다. 이전 local artifact는 진단에만 사용할 수 있고 authoritative baseline으로 간주하지 않는다. +- 검토 과정에서 새 사용자 결정이 필요하면 기존 최대 OQ 번호의 다음 ID를 사용한다. 삭제된 ID를 재사용하지 않으며 모든 선택지는 미선택 checkbox로 두고 추천안을 하나만 표시한다. +- 미해결 OQ가 없으면 `## 미해결 질문`의 본문을 exact `없음`으로 쓴다. +- 추천안을 사용자가 선택한 결정처럼 기록하거나 사용자 대신 checkbox를 선택하지 않는다. + +## 절차 + +1. 요청에서 exact `owner`, `repository`, Issue 번호를 추출하고 단일 안전 경로 요소인지 검증한다. +2. canonical 순서로 Issue lock을 획득하고 Gitea MCP `get_me`로 review role identity를 확인한다. +3. exact Issue와 전체 comments를 읽어 body, `updated_at`, title, state, labels, assignees, milestone과 comment baseline을 immutable snapshot으로 보존한다. +4. 이 스킬의 입력 승인 gate로 current body와 최초 authoring의 원본 provenance comment를 검증한다. 실패하면 comment와 body를 쓰지 않는다. +5. `operations/repository-cache.md`를 새로 읽고 Issue lock을 유지한 채 기존 cache를 검증한다. cache가 없으면 `gitea-repository-cache-bootstrap`을 사용한다. +6. repository lock을 canonical 순서로 획득해 origin을 fetch하고 Gitea default-branch SHA와 local ref가 정확히 일치하는 revision을 pin한다. +7. pinned SHA에서 current Discovery의 모든 저장소 근거, 다섯 후속 문서 경로, 요구사항 ID·파일명과 사용자 prose 수정의 사실성을 조사한다. 움직이는 branch나 다른 workspace를 근거로 사용하지 않는다. +8. 각 OQ의 선택 수를 판정하고 선택된 결정을 관련 모든 섹션에 전파한다. 새 모순·누락·정책 결정이 생기면 OQ 검토 규칙에 따라 새 OQ를 추가한다. +9. `문서 영향 및 산출물`을 README 정책에 따라 전부 다시 판정한다. 요구사항명세서의 exact ID·경로·제목·포함할 내용·제외할 내용과 다른 문서의 구체적 변경 계약을 현재 결정과 pinned SHA에 맞춘다. +10. 조사 종료 시 Gitea default-branch SHA와 local ref가 pinned SHA와 여전히 일치하는지 확인하고 repository lock을 해제한다. Issue lock은 유지한다. +11. trusted workflow directory에 revised body artifact를 만들고 marker, H2 순서, placeholder 부재, OQ ID·checkbox, 결정 전파, 문서 영향, repository evidence와 credential 부재를 검증한다. +12. revised body가 current body와 exact-match하면 comment와 body write 없이 no-op으로 종료한다. +13. write 직전에 Issue와 comments를 다시 읽는다. snapshot의 body·`updated_at` 또는 comment baseline이 drift했으면 외부 write 없이 중단한다. +14. snapshot의 body·`updated_at`·comment baseline과 비본문 필드가 불변일 때 revised body artifact의 exact bytes로 Issue body 하나만 교체한다. title, state, labels, assignees, milestone은 write payload에 포함하지 않는다. +15. Issue와 comments를 다시 읽어 body exact-match, 비본문 필드 불변성과 comment baseline 완전 불변을 검증한다. review는 어떤 comment도 추가·수정·삭제해서는 안 된다. +16. 동일 current state를 다시 판정해 추가 write가 없는 idempotent no-op인지 확인한 뒤 Issue lock을 해제한다. + +## 금지 사항 + +- 사용자의 미선택 OQ를 자동으로 선택하지 않는다. +- 선택된 결정을 근거 없이 확장하거나 다른 OQ의 답으로 간주하지 않는다. +- review 중 Issue를 승인하거나 구현 상태로 전환하지 않는다. +- 검토 전 body 또는 revised body를 comment로 보존하지 않고 어떤 comment도 추가·수정·삭제하지 않는다. +- label, assignee, title, state, milestone 또는 최초 authoring의 원본 provenance comment를 변경하지 않는다. +- 요구사항명세서, data-model, interface-contract, project-structure, runtime-flow 또는 source를 직접 작성하지 않는다. +- stale cache, unpinned source, 다른 workspace, installed package 또는 이전 local artifact를 authoritative 근거로 사용하지 않는다. +- 불확실한 Gitea write를 blind retry하지 않는다. + +## 검증 및 보고 + +완료 전에 다음을 구분해 보고한다. + +- authenticated Gitea identity와 exact target +- Issue/comment pre-write snapshot과 입력 승인 gate 결과 +- cache 경로, bare·remote 검증, fetch와 pinned Gitea/local SHA +- 사용자 선택 OQ, 유지한 OQ, 제거한 OQ와 새로 연 OQ +- 사용자 prose 수정의 수용·조정·거부 결과와 근거 +- 확정된 결정의 섹션별 전파 결과 +- 다섯 후속 문서의 조치·exact 경로·변경 내용 검증 결과 +- revised body exact readback과 비본문 필드 불변성 +- comment baseline 완전 불변과 replay no-op 결과 +- partial 또는 fail-closed 상태 + +프로세스 exit나 MCP write 응답만으로 성공을 주장하지 않는다. authoritative readback 또는 replay 검증이 하나라도 실패하면 completed로 보고하지 않는다. diff --git a/harness/agents/spec/skills/gitea-implementation-spec-review/SKILL.md b/harness/agents/spec/skills/gitea-implementation-spec-review/SKILL.md new file mode 100644 index 0000000..9783337 --- /dev/null +++ b/harness/agents/spec/skills/gitea-implementation-spec-review/SKILL.md @@ -0,0 +1,249 @@ +--- +name: gitea-implementation-spec-review +description: 승인된 계약에 대해 Gitea 구현 PR 적합성을 검토한다. +version: 0.1.0 +author: genichin, Hermes Agent +license: MIT +platforms: [linux] +metadata: + hermes: + tags: [gitea, implementation, pull-request, review, specification] + related_skills: [gitea-discovery-review, gitea-repository-cache-bootstrap] +--- + +# Gitea Implementation Specification Review + +`gitea-impl-agent`가 만든 Gitea PR을 승인된 Discovery 계약과 대조해 요구사항 충족, 범위 누락·초과, acceptance criteria와 test evidence의 대응을 판정한다. 검토 결과는 구현 branch의 Git commit이나 Issue comment가 아니라 검토한 immutable PR head commit에 바인딩된 native Gitea review로 기록한다. 일반 코드 품질·보안 검토, source 수정, Discovery 변경과 merge는 수행하지 않는다. + +## 사용 시점 + +다음과 같이 exact repository, source Issue와 PR을 지정해 구현 적합성 검토를 요청받았을 때 사용한다. + +```text +/ Issue # 구현 PR #을 명세 적합성 기준으로 검토하라 +``` + +이 요청은 아래 조건을 모두 충족할 때 exact PR head에 native review 하나를 작성하는 것만 승인한다. + +- `approve` → `APPROVED` +- `request_changes` → `REQUEST_CHANGES` +- `blocked_spec` → `COMMENT` + +Issue comment·body·title·state·label·assignee·milestone, repository file, branch, commit, merge, release와 deployment write는 승인하지 않는다. + +## 역할과 검토 경계 + +- Spec-agent는 current authoritative Discovery의 확정 계약을 해석하고 구현의 적합성을 판정한다. +- Spec-agent는 review 중 새 요구사항·acceptance criterion·설계 선택을 만들거나 기존 계약을 구현에 맞춰 완화하지 않는다. +- 일반적인 코드 스타일, 광범위한 보안 탐색, 성능 최적화와 유지보수성은 독립 code-review worker의 책임이다. 다만 계약 위반으로 이어지는 논리·보안 문제는 spec finding으로 다룬다. +- 구현 결함과 명세 결함을 분리한다. 명확한 계약 위반은 `request_changes`, 계약 자체의 모순·누락 때문에 판정할 수 없으면 `blocked_spec`이다. +- Review worker를 위임하더라도 main spec-agent가 authoritative evidence, 최종 verdict, native review write와 readback에 책임을 진다. +- Native review는 spec-agent 전용 Gitea MCP connection으로만 작성한다. 해당 connection의 `get_me` stable account ID를 snapshot에 기록하고 ambient REST token, impl-agent credential 또는 다른 profile credential로 fallback하지 않는다. +- 전용 MCP connection이나 expected stable account identity를 검증할 수 없으면 read-only 분석과 native review write를 모두 완료로 보고하지 않는다. + +## 입력 승인 gate + +다음 조건을 모두 충족하지 않으면 native review를 작성하지 않는다. + +- `owner`, `repository`, Issue 번호와 PR 번호가 각각 안전한 canonical identifier다. +- Gitea MCP `get_me`로 확인한 stable account ID가 spec review role이며 PR author·implementation role의 stable account ID와 다르다. Transport별 login 표시가 달라도 account ID가 같으면 같은 identity로 판정한다. +- PR이 open이고 exact target repository의 default branch를 base로 하며 head repository·branch와 40자리 full head SHA를 authoritative하게 식별할 수 있다. Fork PR이면 head repository metadata와 branch SHA를 별도로 읽어 PR head와 일치시킨다. +- PR body에 source Issue와 결합되는 exact `Closes #`가 하나만 있고 다른 close directive가 없다. +- Source Issue body 첫 줄이 exact ``이며 canonical H2 구조를 만족한다. +- `## 미해결 질문`이 exact `없음`이고 선택되지 않은 checkbox, placeholder, 충돌 결정 또는 실제 credential이 없다. +- Discovery pinned revision, exact 산출물 경로와 요구사항 ID를 판독할 수 있다. +- PR base·head, commits, changed files, bounded diff, status와 기존 native reviews를 authoritative API에서 읽을 수 있다. +- Changed-file pagination, review pagination 또는 diff가 incomplete·truncated·ambiguous하지 않다. +- 동일 spec-agent가 같은 head·contract에 이미 게시한 canonical review가 0개 또는 정확히 1개다. 둘 이상이거나 서로 다른 결과가 있으면 fail-closed한다. + +## 계약 snapshot과 binding + +1. Source Issue의 number, author, title, body, state, labels, assignees, milestone, `created_at`, `updated_at`, 전체 comments와 body SHA-256을 immutable contract snapshot으로 기록한다. +2. PR number, state, base repository·branch·SHA, head repository·branch·SHA, author, body, head commit object, changed-file set와 기존 review baseline을 immutable PR snapshot으로 기록한다. +3. `contract-sha256`은 authoritative Issue body의 exact UTF-8 bytes에 대해 계산한다. +4. Review는 exact `owner/repository`, Issue, PR, `head`, `contract-sha256`와 `result-sha256`에 결합한다. `correlation`은 이 deduplication identity의 canonical NUL-delimited UTF-8 bytes를 SHA-256한 deterministic 64자리 hex이며 run마다 바꾸지 않는다. +5. Issue body·`updated_at`, PR head, base target 또는 review baseline이 write 전에 drift하면 결과를 새 상태에 자동 적용하지 않고 중단한다. + +## Acceptance matrix + +Discovery의 포함 범위, 제외 범위, 제약 조건, 확정된 결정과 `문서 영향 및 산출물`에서 판정 가능한 항목을 추출한다. 각 항목을 다음 필드로 기록한다. + +```text +criterion: +contract: +implementation_paths: [...] +test_paths: [...] +evidence: +result: satisfied | unsatisfied | indeterminate +reason: +``` + +- 모든 확정 결정과 필수 산출물은 criterion 하나 이상에 대응해야 한다. +- 모든 changed file은 criterion, 필수 supporting change 또는 검증 evidence로 설명돼야 한다. +- `영향 없음`으로 판정된 문서·schema·domain 영역의 변경은 범위 초과 후보로 조사한다. +- PR의 요구사항 문서는 Discovery가 배정한 ID·경로·제목을 유지하고 `gitea-agent.requirement/v1` canonical 계약, Issue `#`와 구현 PR traceability를 만족해야 한다. +- Acceptance criterion마다 구현 위치와 test·status·정적 evidence가 있어야 한다. PR body의 test 성공 주장만으로 실제 file·status 또는 실행 evidence를 대체하지 않는다. +- CI/status evidence는 exact reviewed head SHA에 결합돼야 한다. Command evidence는 command, exit code, 판정에 필요한 output, 실행 환경과 source revision의 trusted provenance를 포함해야 하며 하나라도 없으면 `indeterminate`다. +- 실패 경로, fail-closed, no-call·no-write boundary와 credential 비노출 조건은 positive-path test만으로 충족됐다고 판정하지 않는다. +- 현재 harness에 spec-review 전용 clean worktree 계약이 없으므로 구현 Issue worktree를 spec role로 재사용하거나 임시 clone·checkout을 만들지 않는다. 독립 실행이 필요한 criterion에 authoritative CI/status 또는 신뢰 가능한 실행 evidence가 없으면 이를 숨기지 않고 `indeterminate`로 기록한다. + +## Verdict 규칙 + +### `approve` + +다음을 모두 충족할 때만 사용한다. + +- 모든 필수 criterion이 `satisfied`다. +- `unsatisfied`와 `indeterminate`가 없다. +- 모든 changed file이 승인 범위로 설명된다. +- 필수 문서, source, 설정과 test가 존재한다. +- PR head와 contract snapshot에 drift가 없다. + +Native review state는 `APPROVED`다. + +### `request_changes` + +명확한 승인 계약에 대해 다음 중 하나 이상이 확인될 때 사용한다. + +- 필수 기능·문서·test 또는 acceptance criterion 누락 +- 구현 동작이 확정 결정, fail-closed 또는 no-call·no-write boundary를 위반 +- 제외 범위 구현 또는 설명할 수 없는 범위 초과 변경 +- 잘못된 요구사항 ID·경로·traceability +- 실행 또는 정적 evidence가 계약 실패를 증명 + +Native review state는 `REQUEST_CHANGES`다. 변경 요청은 기존 계약의 exact 근거를 인용하고 새로운 요구사항을 만들지 않는다. + +### `blocked_spec` + +다음처럼 구현만 수정해서는 안전하게 해결할 수 없을 때 사용한다. + +- 승인 계약 내부의 모순 +- 둘 이상의 비호환 구현 중 선택이 필요한 누락 결정 +- acceptance criterion이 판정 불가능하거나 source reality와 충돌 +- 현재 contract snapshot의 provenance 또는 scope가 불명확 + +Native review state는 `COMMENT`다. 구현 에이전트의 결함으로 표현하거나 임의 선택을 제안하지 않고 Discovery 재검토가 필요하다고 명시한다. Issue나 Discovery는 이 스킬에서 변경하지 않는다. + +`indeterminate`가 단순 구현 evidence 누락이면 `request_changes`, 명세 자체의 결함이면 `blocked_spec`으로 구분한다. + +## Strict review result artifact + +Canonical body를 렌더링하기 전에 trusted workflow directory에 JSON object artifact를 만든다. Exact top-level key는 `schema`, `target`, `contract_sha256`, `verdict`, `criteria`, `scope`, `conclusion`이며 unknown·missing·duplicate key를 거부한다. + +- `schema`는 string exact `gitea-agent.implementation-spec-review-result/v1`이다. +- `target`은 object이고 exact key는 `owner`, `repository`, `issue`, `pull`, `head`다. `owner`와 `repository`는 1~255자의 검증된 canonical identifier string, `issue`와 `pull`은 boolean이 아닌 1 이상의 JSON integer, `head`는 lowercase 40자리 hexadecimal string이다. +- `contract_sha256`은 lowercase 64자리 hexadecimal string이다. +- `verdict`는 string enum `approve|request_changes|blocked_spec` 중 하나다. +- `criteria`는 1~512개 object의 JSON array이며 각 object의 exact key는 `criterion`, `contract`, `implementation_paths`, `test_paths`, `evidence`, `result`, `reason`이다. +- `criterion`은 1~200자, `contract`, `evidence`, `reason`은 각각 1~4000자의 string이다. `implementation_paths`와 `test_paths`는 각각 0~512개의 중복 없는 string array다. +- `result`는 string enum `satisfied|unsatisfied|indeterminate` 중 하나다. +- 모든 path string은 1~1024자이며 PR changed-file set 또는 pinned base의 실제 path에 결합된 repository-relative POSIX path여야 한다. Absolute path, 빈 segment, `.`, `..`, backslash, NUL과 control character를 거부한다. +- `scope`는 object이고 exact key는 `missing`, `excess`, `spec_conflicts`다. 각 값은 0~512개의 중복 없는 1~4000자 string array다. +- `conclusion`은 1~4000자의 string이다. 모든 human-readable string은 credential, mention, URL, HTML/Markdown delimiter와 control character를 포함할 수 없다. +- Criteria 순서는 Discovery에서 추출한 계약 순서이며 같은 criterion을 중복할 수 없다. +- `approve`에는 모든 criterion이 `satisfied`이고 세 scope array가 비어 있어야 한다. `request_changes`에는 `unsatisfied` 또는 구현 evidence 누락으로 인한 `indeterminate`가 있어야 한다. `blocked_spec`에는 `spec_conflicts` 또는 명세 결함으로 인한 `indeterminate`가 있어야 한다. +- JSON parser는 duplicate key를 거부하고 exact type을 검사한다. 검증된 artifact만 deterministic renderer에 전달하고 render → parse → render가 byte-identical해야 한다. + +## Canonical native review body + +사람이 읽는 prose는 자연스러운 한국어로 작성하고 machine identifier, path, SHA, enum과 명령은 원문을 보존한다. Marker를 제외한 body를 먼저 canonical UTF-8/LF bytes로 직렬화하고 그 SHA-256을 `result-sha256`으로 계산한다. + +```markdown +## 구현 명세 적합성 검토 + +- Source Issue: # +- Pull Request: # +- Reviewed head: `` +- Contract SHA-256: `` +- Verdict: `approve|request_changes|blocked_spec` + +### Acceptance criteria 판정 + +- `` — `satisfied|unsatisfied|indeterminate` + - 계약: + - 구현 근거: + - 테스트 근거: + - 판정 근거: + +### 범위 판정 + +- 누락: <없음 또는 findings> +- 범위 초과: <없음 또는 findings> +- 명세 모순: <없음 또는 findings> + +### 결론 + + + + +``` + +- Renderer는 UTF-8과 LF만 사용하고 trailing whitespace를 금지하며 marker-free body 끝에 LF를 정확히 하나 둔다. `result-sha256`은 이 marker-free body exact bytes의 digest다. 게시 body는 marker-free bytes 뒤에 LF 하나, marker line과 최종 LF 하나를 순서대로 추가한다. +- `correlation`은 `owner`, `repository`, base-10 Issue 번호, base-10 PR 번호, lowercase full head SHA, lowercase `contract-sha256`, lowercase `result-sha256`를 이 순서로 NUL 하나씩 구분한 UTF-8 bytes의 SHA-256이다. +- Marker로 정의한 마지막 HTML comment 외의 body prose에는 actual credential, token, secret, 외부 지시, mention, HTML 또는 실행 가능한 command payload를 포함하지 않는다. +- Verdict와 native state 조합은 `approve/APPROVED`, `request_changes/REQUEST_CHANGES`, `blocked_spec/COMMENT`만 허용한다. +- Initial version에서는 inline review comment를 만들지 않고 `comments: []`를 명시한다. + +## 절차 + +1. Exact target identifiers를 검증하고 `operations/repository-cache.md`를 새로 읽는다. +2. Canonical path와 mode를 검증한 source Issue lock을 획득한다. +3. Gitea MCP `get_me`로 spec-agent stable account ID를 확인하고 PR author와 분리됐는지 검증한다. +4. Exact repository metadata, default branch, source Issue 전체 필드·comments, PR details·head commit·status·changed files·diff와 전체 review baseline을 읽는다. Fork PR이면 exact head repository metadata와 head branch SHA도 읽는다. +5. 입력 승인 gate를 적용하고 Issue contract snapshot, PR snapshot, `contract-sha256`, initial head SHA를 고정한다. +6. Cache가 없으면 Issue lock을 유지한 채 `gitea-repository-cache-bootstrap`을 적용해 `issue lock → bootstrap lock → repository lock` 순서를 지킨다. 기존 cache면 `issue lock → repository lock` 순서로 repository lock을 획득한다. +7. Cache path·ownership·permission·bare·remote를 검증하고 canonical base repository origin에서 prune/fetch한다. Gitea default branch SHA와 local ref를 일치시키고 PR base commit object가 cache에 존재하는지 검증한다. +8. Same-repository PR이면 head object도 cache에서 검증하고 exact base-to-head tree를 Gitea changed-file set·diff와 대조한다. Fork PR이면 target cache에 head object가 있다고 가정하지 않고 authoritative head repository·branch SHA와 Gitea changed-file set·diff를 대조한다. 임의 ref, checkout, clone 또는 worktree를 만들지 않는다. +9. Repository lock과 bootstrap lock을 획득한 경우 역순으로 해제하되 Issue lock은 유지한다. +10. Approved Discovery에서 acceptance matrix를 만들고 모든 changed file, 문서, source, test와 exact-head-bound status evidence를 대응시킨다. +11. 독립 code-review worker 결과가 있으면 exact head에 바인딩됐고 authoritative하게 읽을 수 있을 때만 보조 evidence로 사용한다. 그 결과를 spec verdict로 그대로 복사하지 않는다. +12. Verdict 규칙으로 하나의 strict result artifact와 canonical review body를 trusted workflow directory에 생성한다. +13. Exact key/type, duplicate key, marker field, digest, deterministic correlation, criterion cardinality, path binding, verdict/state 조합, credential 부재와 render → parse → render byte equality를 검증한다. +14. 여기까지는 read-only preflight다. Exact review 요청이 native review write 권한을 부여하지 않았거나 preflight가 완전하지 않으면 종료하며 review create 또는 다른 mutation을 수행하지 않는다. +15. Write 직전에 Issue 전체 필드·comments, PR details·base·head·changed files와 전체 review baseline을 다시 읽는다. Contract snapshot, base target, head SHA, changed-file set 또는 baseline이 drift하면 native review를 쓰지 않는다. +16. Stable reviewer account ID, head, contract digest와 result digest가 모두 같은 canonical review가 정확히 하나 이미 있으면 exact review와 review comments를 authoritative하게 readback하고 write 없는 no-op으로 종료한다. 같은 binding의 다른 결과 또는 중복 review가 있으면 fail-closed한다. +17. Gitea MCP `pull_request_review_write(method=create)`를 `body=`, `comments=[]`, `commit_id=`, `state=`로 정확히 한 번 호출한다. `state`는 verdict mapping과 exact-match해야 한다. +18. Create 응답 성공만 신뢰하지 않는다. `get_review`, complete `get_reviews` pagination과 exact review의 `get_review_comments`를 다시 읽어 baseline 보존과 exact 신규 review 하나, stable author account ID·body·state·commit·timestamp·stale/dismissed 상태 및 inline comment가 0개임을 검증한다. +19. Review create가 timeout·오류 또는 malformed response이면 blind retry하지 않는다. Complete review collection과 candidate review comments를 한 번 authoritative reconciliation해 baseline 대비 exact result가 하나이고 inline comment가 없을 때만 committed로 판정한다. 없거나 여러 개·경쟁 review가 있으면 uncertain/partial로 중단한다. +20. Source Issue와 PR을 다시 읽는다. Body·title·state·labels·assignees·milestone·comments, PR base·head·changed files·merge state와 source content는 불변이어야 한다. 단, Gitea가 exact native review 생성의 파생 projection으로만 다음을 변경한 경우는 허용하고 별도 mutation으로 보고하지 않는다: Issue와 PR `updated_at`이 verified review `submitted_at`으로 갱신됨, PR `review_comments`가 top-level review를 포함해 baseline보다 정확히 1 증가함, `requested_reviewers`에 verified review author stable account ID가 정확히 추가됨. `comments: []` 계약은 이 projection count와 별개로 authoritative `get_review_comments`가 0개여야 한다. 이 세 파생 변경은 create 전후 snapshot과 exact review ID·author·timestamp로 인과를 증명해야 하며 다른 field 변경, 기존 reviewer 제거, 추가 account 또는 별도 write call이 있으면 fail-closed한다. +21. 동일 state를 read-only replay해 stable deduplication identity의 canonical review를 재사용하며 `writes=[]`인지 확인한다. +22. Issue lock을 해제한 뒤 `repository.lock`과 `issue-.lock`을 각각 non-blocking으로 획득·해제할 수 있는지 확인한다. + +## 위임 규칙 + +- Main spec-agent는 contract extraction, acceptance matrix, verdict와 외부 write에 책임을 진다. +- Bounded diff 분할, criterion별 evidence 수집과 일반 코드 품질 검토는 독립 delegate에 병렬 위임할 수 있다. +- Delegate에는 exact PR head, approved contract subset, changed-file allowlist와 read-only 범위를 제공한다. +- Delegate가 Gitea write, source 수정, Git commit·push·merge, lock·cache·worktree 변경을 수행하지 못하게 한다. +- Delegate summary는 evidence가 아니다. Main agent가 cited path, diff, status와 test evidence를 직접 확인한다. + +## 금지 사항 + +- 구현 branch에 review file, Git commit, tag 또는 ref를 만들지 않는다. +- Source, test, 요구 문서, Discovery 또는 Issue field를 수정하지 않는다. +- Issue comment로 native review를 대체하거나 review 결과를 중복 게시하지 않는다. +- PR을 merge·close·reopen하거나 reviewer·assignee·label을 변경하지 않는다. +- Review 중 새 요구사항, acceptance criterion, 설계 선택 또는 범위 확장을 만들지 않는다. +- PR author와 같은 identity로 self-review하지 않는다. +- Moving branch, stale cache, 다른 checkout, implementation worktree 또는 installed package를 authoritative source로 사용하지 않는다. +- Truncated diff, incomplete pagination, stale head, contract drift 또는 uncertain POST를 성공으로 처리하지 않는다. +- Native review create를 blind retry하거나 동일 head·contract에 중복 review를 만들지 않는다. +- Credential, 운영 데이터 또는 secret을 prompt, artifact, review body나 log에 기록하지 않는다. + +## 검증 및 보고 + +완료 보고에는 다음을 구분해 포함한다. + +- authenticated spec-agent identity와 PR author 분리 +- exact repository, source Issue, PR과 `Closes #` binding +- contract snapshot `updated_at`, body digest와 unresolved-question 검증 +- PR base·head·state, changed-file/diff/review pagination completeness +- cache path, bare·remote 검증, fetch와 Gitea/local SHA +- criterion별 `satisfied|unsatisfied|indeterminate`, 구현·test evidence +- 누락, 범위 초과, 명세 모순과 최종 verdict 근거 +- native review state, ID, author, body digest, commit binding과 timestamp readback +- Issue·comments·PR head 불변성과 review baseline 대비 cardinality +- read-only replay `writes=[]` +- partial, blocked 또는 fail-closed 상태 + +Review write 응답, process exit, PR body의 test 주장, delegate 보고 또는 native state만으로 완료를 주장하지 않는다. Exact review body·commit binding과 authoritative collection readback, 외부 상태 불변성과 replay가 모두 확인된 경우에만 완료로 판정한다. diff --git a/harness/agents/spec/skills/gitea-repository-cache-bootstrap/SKILL.md b/harness/agents/spec/skills/gitea-repository-cache-bootstrap/SKILL.md new file mode 100644 index 0000000..e7dd701 --- /dev/null +++ b/harness/agents/spec/skills/gitea-repository-cache-bootstrap/SKILL.md @@ -0,0 +1,56 @@ +--- +name: gitea-repository-cache-bootstrap +description: 새 Gitea repository cache를 안전하게 초기화한다. +version: 0.1.0 +author: genichin, Hermes Agent +license: MIT +platforms: [linux] +metadata: + hermes: + tags: [gitea, repository, cache, bootstrap] + related_skills: [] +--- + +# Gitea Repository Cache Bootstrap + +새 Gitea repository cache를 생성하거나 기존 cache가 초기화됐는지 검증한다. 모든 경로, lock, Git 및 credential 규칙은 [Repository cache 운영 지침](../../../../operations/repository-cache.md)만 정본으로 사용하며 이 스킬에 복제하거나 완화하지 않는다. + +## 사용 시점 + +- `/opt/data/gitea-agent/repositories///repository.git`이 없을 때 +- 정확한 `owner/repository`의 cache 초기화를 요청받았을 때 +- 기존 cache가 해당 Gitea repository의 유효한 bare mirror인지 확인할 때 + +Issue 본문 변경, comment 작성, checkout, worktree 생성 또는 source 수정에는 사용하지 않는다. + +## 절차 + +1. `read_file`로 linked canonical 문서 `operations/repository-cache.md`를 새로 읽는다. +2. 요청에서 exact `owner/repository`를 추출하고 canonical 지침의 identifier·path 검증을 수행한다. +3. Gitea MCP로 authenticated role, exact repository metadata, clone URL, default branch와 SHA를 읽는다. +4. canonical bootstrap lock을 획득하고 target 상태를 다시 확인한다. +5. target이 없으면 canonical 신규 cache 초기화 절차를 그대로 수행한다. target이 있으면 삭제하거나 덮어쓰지 않고 canonical 기존 cache 검증 절차를 수행한다. +6. final target을 새로 읽어 path safety, bare 상태, remote identity, default branch와 SHA를 검증한다. +7. credential helper와 현재 run의 staging이 남지 않았는지 확인하고 lock을 역순으로 해제한다. +8. local 결과와 Gitea authoritative evidence를 분리해 보고한다. + +## 실패 처리 + +- canonical 지침을 읽을 수 없거나 대상이 불명확하면 아무것도 생성하지 않는다. +- malformed, symlink, identity drift 또는 SHA drift 상태를 자동 수정하지 않는다. +- clone/fetch 결과가 불확실하면 completed로 보고하지 않는다. +- 현재 run 소유임을 증명할 수 없는 staging이나 target을 삭제하지 않는다. +- credential을 inherited environment나 다른 role에서 fallback하지 않는다. + +## 검증 + +완료 전에 다음을 모두 확인한다. + +- 현재 작업에서 `operations/repository-cache.md`를 읽었다. +- Gitea identity와 exact repository metadata가 확인됐다. +- final `repository.git`이 bare mirror다. +- remote identity가 exact `owner/repository`와 일치한다. +- Gitea와 local default-branch SHA가 일치한다. +- final path의 유형, ownership과 permissions가 canonical 지침을 충족한다. +- staging, credential helper와 획득한 advisory lock이 남지 않았다. +- Gitea external write가 발생하지 않았다. diff --git a/harness/docs/data-model/README.md b/harness/docs/data-model/README.md new file mode 100644 index 0000000..64c6913 --- /dev/null +++ b/harness/docs/data-model/README.md @@ -0,0 +1,22 @@ +# 데이터 모델 작성 방법 + +데이터 모델 문서는 승인된 Discovery와 구현 결과를 근거로 구현 완료 후의 정본 데이터 구조를 설명한다. + +## 저장 위치 + +완성된 데이터 모델 문서는 대상 저장소 루트 기준 `docs/data-model.md`에 저장한다. 파일이 이미 존재하면 별도 문서를 만들지 않고 이 정본 문서를 갱신한다. + +이 디렉터리의 [`README.md`](README.md)와 [`TEMPLATE.md`](TEMPLATE.md)는 하네스의 작성 규칙과 템플릿이며 대상 저장소에 저장하는 산출물이 아니다. + +## 작성 방법 + +1. [`TEMPLATE.md`](TEMPLATE.md)를 복사하고 섹션 순서를 유지한다. +2. 목적과 포함·제외 범위를 먼저 확정한다. +3. `정본 엔터티`의 H3 블록을 엔터티 수만큼 복제하고, 각 엔터티의 필드 행을 실제 필드 수만큼 복제한다. +4. 모든 `${lowercase_snake_case}` placeholder를 실제 값으로 교체한다. `${stable_section_id}`에는 고유한 소문자 케밥 표기법을 사용한다. 엔터티 이름이 바뀌어도 ID는 유지하며 삭제된 ID를 재사용하지 않는다. +5. 각 필드 행에 필드명, 타입, 필수 여부, 기본값, 허용 값·형식, 단위·정밀도 및 민감정보 여부를 기록한다. 해당 값이 없으면 `없음`으로 기록한다. +6. 관계의 방향과 카디널리티, 소유권, 삭제 규칙 및 항상 유지해야 하는 불변조건을 기록한다. +7. 저장 위치와 직렬화 형식, 기존 데이터 마이그레이션, 하위 호환성 및 롤백 조건을 기록한다. +8. 정상·경계·오류 데이터와 저장 후 재조회·마이그레이션 검증 방법을 기록한다. + +근거 없이 엔터티나 필드를 추측하지 않는다. 해당 사항이 없으면 `없음`으로 기록하고, 완성 문서에는 placeholder나 작성 안내 문구를 남기지 않는다. diff --git a/harness/docs/data-model/TEMPLATE.md b/harness/docs/data-model/TEMPLATE.md new file mode 100644 index 0000000..abb9ff4 --- /dev/null +++ b/harness/docs/data-model/TEMPLATE.md @@ -0,0 +1,51 @@ +# ${data_model_title} + +## 목적과 범위 + +${purpose} + +- 포함 범위: ${included_scope} +- 제외 범위: ${excluded_scope} + +## 정본 엔터티 + +### ${stable_section_id} — ${entity_name} + +- 책임: ${entity_responsibility} +- 식별자: `${identifier_field}` +- 생명 주기: ${entity_lifecycle} + +#### 필드 + +| 필드 | 타입 | 필수 여부 | 기본값 | 허용 값·형식 | 단위·정밀도 | 민감정보 | +|---|---|---|---|---|---|---| +| `${field_name}` | `${field_type}` | ${required_or_optional} | ${default_value_or_none} | ${allowed_values_and_format} | ${unit_and_precision_or_none} | ${sensitivity_or_none} | + +## 관계와 불변조건 + +### 관계 + +| 출발 엔터티 | 관계 | 대상 엔터티 | 카디널리티 | 필수 여부 | 소유권 | 삭제 규칙 | +|---|---|---|---|---|---|---| +| `${source_entity_id}` | ${relationship} | `${target_entity_id}` | `${cardinality}` | ${required_or_optional} | ${ownership} | ${deletion_rule} | + +### 불변조건 + +- ${invariant} + +## 저장과 마이그레이션 + +- 저장 위치: ${storage_location} +- 직렬화 형식: ${serialization_format} +- 스키마 버전: ${schema_version} +- 기존 데이터 처리: ${existing_data_migration} +- 하위 호환성: ${backward_compatibility} +- 롤백 조건: ${rollback_conditions} + +## 검증 + +- 정상 데이터: ${normal_data_validation} +- 경계 데이터: ${boundary_data_validation} +- 오류 데이터: ${invalid_data_validation} +- 저장 후 재조회: ${round_trip_validation} +- 마이그레이션: ${migration_validation} diff --git a/harness/docs/discovery/README.md b/harness/docs/discovery/README.md new file mode 100644 index 0000000..b36531b --- /dev/null +++ b/harness/docs/discovery/README.md @@ -0,0 +1,66 @@ +# Discovery 작성 방법 + +Discovery는 Gitea Issue 본문으로 작성한다. + +1. [`TEMPLATE.md`](TEMPLATE.md)를 복사한다. +2. 첫 번째 marker와 섹션 순서를 유지한다. +3. `${...}` placeholder를 저장소 근거에 기반한 한국어 내용으로 교체한다. +4. 템플릿에 없는 hidden state, marker 또는 metadata를 추가하지 않는다. +5. 미해결 질문이 없으면 `없음`으로 작성한다. + +## Discovery marker 정책 + +``는 Discovery body의 문서 유형과 템플릿 버전을 식별하는 marker다. + +- body의 첫 줄에 exact bytes로 한 번만 둔다. +- marker에 속성, 상태 또는 payload를 추가하지 않는다. +- 작성·검증·재실행 시 marker와 템플릿의 섹션 순서를 함께 확인한다. +- marker가 없거나 값이 다르거나 알 수 없는 버전이면 유효한 Discovery 초안으로 취급하지 않는다. +- 다른 버전을 임의로 생성하거나 자동 변환하지 않는다. + +각 섹션에는 요약, 문제, 포함·제외 범위, 제약 조건, 확정된 결정, 저장소 근거, 문서 영향과 미해결 질문을 기록한다. + +## 문서 영향 및 산출물 작성 방법 + +`문서 영향 및 산출물`에는 다음 문서 유형을 순서대로 모두 기록한다. 영향이 없는 문서도 생략하지 않는다. + +1. 요구사항명세서 — `docs/requirements/req-NNN_.md` 형식의 exact 경로 +2. 데이터 모델 — `docs/data-model.md` +3. 인터페이스 계약 — `docs/interface-contract.md` +4. 프로젝트 구조 — `docs/project-structure.md` +5. 런타임 흐름 — `docs/runtime-flow.md` + +각 문서 유형은 다음 형식으로 작성한다. + +```markdown +### 문서 이름 + +- 조치: 신규 작성 | 갱신 | 영향 없음 +- 경로: `대상 저장소 기준 경로` +- 변경 내용: 추가하거나 변경할 계약과 범위 +- 근거: Discovery 범위 또는 pinned source revision에서 확인한 저장소 근거 +``` + +- `조치`는 세 값 중 하나만 사용한다. +- pinned source revision에 정본 문서가 없고 문서 영향이 있으면 `신규 작성`, 정본 문서가 있고 문서 영향이 있으면 `갱신`으로 기록한다. +- `영향 없음`인 경우에도 변경 내용에 영향이 없는 이유를 기록한다. +- 요구사항명세서는 pinned source revision의 기존 `docs/requirements/req-NNN_.md` 파일을 조사해 충돌하지 않는 `REQ-NNN`을 결정하고, 확정된 범위를 나타내는 소문자 ASCII 케밥 표기법의 slug를 사용해 exact 경로를 기록한다. README와 TEMPLATE는 할당된 요구사항 ID로 세지 않는다. +- 기존 요구사항 파일이 없으면 `REQ-001`부터 시작한다. 기존 ID가 `REQ-001`부터 빠짐없이 연속이면 가장 큰 ID의 다음 값을 사용한다. 기존 ID가 중복·비연속·형식 오류 상태이거나 exact 파일명을 안전하게 결정할 수 없으면 값을 추측하지 않고 Discovery 작성을 중단한다. +- 요구사항명세서 항목에는 `문서 ID`, `제목`, `포함할 내용`, `제외할 내용`을 추가한다. 포함할 내용은 구현 에이전트가 요구사항과 acceptance criteria로 내려야 할 동작, 오류, 보안, 데이터·인터페이스, 호환성 및 추적성 계약을 구체적으로 나열한다. +- 데이터 모델은 변경할 엔터티·필드·관계·불변조건·마이그레이션을, 인터페이스 계약은 입력·출력·인증·오류·호환성을, 프로젝트 구조는 경로·책임·의존 방향·배치 규칙을, 런타임 흐름은 시작 조건·단계·상태 변경·외부 호출·실패 복구·동시성·관찰 가능성을 변경 내용에 기록한다. +- 문서가 `영향 없음`이면 해당 문서 계약이 왜 바뀌지 않는지 확정 범위와 저장소 근거를 사용해 설명한다. +- 변경 내용에는 구현 에이전트가 문서에 반영할 계약과 범위만 기록한다. Discovery 작성 중 후속 문서를 생성하거나 수정하지 않는다. +- 저장소 근거나 확정된 결정 없이 엔터티, 인터페이스, 디렉터리, 실행 흐름 또는 문서 변경을 만들어내지 않는다. + +## OQ(${open_questions}) 작성 방법 + +각 OQ에는 질문에 대한 설명과 선택지를 작성하고 추천안을 표시한다. 모든 선택지는 체크박스(`[ ]`)를 사용한다. + +```markdown +### OQ-001 — 질문 + +질문에 대한 설명 + +- [ ] 선택지 A (추천) +- [ ] 선택지 B +``` diff --git a/harness/docs/discovery/TEMPLATE.md b/harness/docs/discovery/TEMPLATE.md new file mode 100644 index 0000000..be869d7 --- /dev/null +++ b/harness/docs/discovery/TEMPLATE.md @@ -0,0 +1,36 @@ + +## 요약 + +${summary} + +## 문제 + +${problem} + +## 포함 범위 + +${in_scope} + +## 제외 범위 + +${out_of_scope} + +## 제약 조건 + +${constraints} + +## 확정된 결정 + +${confirmed_decisions} + +## 저장소 근거 + +${repository_evidence} + +## 문서 영향 및 산출물 + +${documentation_impact} + +## 미해결 질문 + +${open_questions} diff --git a/harness/docs/interface-contract/README.md b/harness/docs/interface-contract/README.md new file mode 100644 index 0000000..6a0c03d --- /dev/null +++ b/harness/docs/interface-contract/README.md @@ -0,0 +1,22 @@ +# 인터페이스 계약 작성 방법 + +인터페이스 계약 문서는 승인된 Discovery와 구현 결과를 근거로 외부·내부 인터페이스의 호출 규칙을 설명한다. + +## 저장 위치 + +완성된 인터페이스 계약 문서는 대상 저장소 루트 기준 `docs/interface-contract.md`에 저장한다. 파일이 이미 존재하면 별도 문서를 만들지 않고 이 정본 문서를 갱신한다. + +이 디렉터리의 [`README.md`](README.md)와 [`TEMPLATE.md`](TEMPLATE.md)는 하네스의 작성 규칙과 템플릿이며 대상 저장소에 저장하는 산출물이 아니다. + +## 작성 방법 + +1. [`TEMPLATE.md`](TEMPLATE.md)를 복사하고 섹션 순서를 유지한다. +2. 목적, 포함·제외 범위 및 인터페이스 사용 주체를 명시한다. +3. `계약`의 H3 블록을 인터페이스 수만큼 복제하고 입력·출력·오류 행을 실제 계약 수만큼 복제한다. +4. 모든 `${lowercase_snake_case}` placeholder를 실제 값으로 교체한다. `${stable_section_id}`에는 고유한 소문자 케밥 표기법을 사용한다. 인터페이스 이름이 바뀌어도 ID는 유지하며 삭제된 ID를 재사용하지 않는다. +5. 각 인터페이스의 책임, 종류·프로토콜, 호출 대상, 버전, 호출 주체, 인증·권한을 기록한다. +6. 입력·출력 필드와 오류 계약을 표에 기록한다. 값이 없으면 `없음`으로 기록한다. +7. 기존 호출 방식, 버전 변경·폐기, 재시도·중복 처리 및 순서 보장에 대한 호환성 계약을 기록한다. +8. 실제 구현과 일치하는 호출 예시와 정상·실패·경계 계약의 검증 방법을 기록한다. 예시에는 실제 credential이나 민감정보를 포함하지 않는다. + +근거 없이 인터페이스나 동작을 추측하지 않는다. 완성 문서에는 placeholder나 작성 안내 문구를 남기지 않는다. diff --git a/harness/docs/interface-contract/TEMPLATE.md b/harness/docs/interface-contract/TEMPLATE.md new file mode 100644 index 0000000..d144aae --- /dev/null +++ b/harness/docs/interface-contract/TEMPLATE.md @@ -0,0 +1,73 @@ +# ${interface_contract_title} + +## 목적과 범위 + +${purpose} + +- 포함 범위: ${included_scope} +- 제외 범위: ${excluded_scope} +- 사용 주체: ${consumers} + +## 계약 + +### ${stable_section_id} — ${interface_name} + +- 책임: ${responsibility} +- 종류·프로토콜: ${interface_type_and_protocol} +- 호출 대상·operation: ${target_and_operation} +- 버전: ${version} +- 호출 주체: ${caller} +- 인증 방식: ${authentication} +- 필요 권한: ${authorization} + +#### 입력 + +| 필드 | 위치 | 타입 | 필수 여부 | 기본값 | 허용 값·형식 | 의미 | 민감정보 | +|---|---|---|---|---|---|---|---| +| `${input_field_name}` | ${input_location} | `${input_type}` | ${input_required_or_optional} | ${input_default_or_none} | ${input_constraints} | ${input_meaning} | ${input_sensitivity_or_none} | + +#### 출력 + +| 필드 | 위치 | 타입 | 필수 여부 | 허용 값·형식 | 의미 | 민감정보 | +|---|---|---|---|---|---|---| +| `${output_field_name}` | ${output_location} | `${output_type}` | ${output_required_or_optional} | ${output_constraints} | ${output_meaning} | ${output_sensitivity_or_none} | + +#### 오류 + +| 코드 | 분류 | 발생 조건 | 재시도 가능 여부 | 호출자 대응 | 실패 시 상태 변경 | +|---|---|---|---|---|---| +| `${error_code}` | ${error_class} | ${error_condition} | ${retryability} | ${caller_action} | ${state_change_or_none} | + +## 호환성 + +- 유지할 기존 호출 방식: ${existing_call_compatibility} +- 버전 변경 정책: ${versioning_policy} +- 폐기 정책: ${deprecation_policy} +- 재시도 계약: ${retry_contract} +- 중복 처리 계약: ${idempotency_contract} +- 순서 보장: ${ordering_guarantee} + +## 예시 + +### ${example_id} — ${example_title} + +- 대상 인터페이스: `${target_interface_id}` + +#### 호출 + +```text +${request_or_call_example} +``` + +#### 결과 + +```text +${response_or_result_example} +``` + +## 검증 + +- 정상 계약: ${success_contract_validation} +- 실패 계약: ${failure_contract_validation} +- 경계 계약: ${boundary_contract_validation} +- 구현·스키마·예시 정합성: ${implementation_schema_example_consistency} diff --git a/harness/docs/project-structure/README.md b/harness/docs/project-structure/README.md new file mode 100644 index 0000000..a8cb59e --- /dev/null +++ b/harness/docs/project-structure/README.md @@ -0,0 +1,22 @@ +# 프로젝트 구조 작성 방법 + +프로젝트 구조 문서는 승인된 Discovery와 구현 결과를 근거로 저장소의 구성 원칙, 영역별 책임 및 의존 경계를 설명한다. + +## 저장 위치 + +완성된 프로젝트 구조 문서는 대상 저장소 루트 기준 `docs/project-structure.md`에 저장한다. 파일이 이미 존재하면 별도 문서를 만들지 않고 이 정본 문서를 갱신한다. + +이 디렉터리의 [`README.md`](README.md)와 [`TEMPLATE.md`](TEMPLATE.md)는 하네스의 작성 규칙과 템플릿이며 대상 저장소에 저장하는 산출물이 아니다. + +## 작성 방법 + +1. [`TEMPLATE.md`](TEMPLATE.md)를 복사하고 섹션 순서를 유지한다. +2. 목적과 포함·제외 경계를 먼저 확정한다. +3. `디렉터리 구조`의 H3 블록을 주요 영역 수만큼 복제하고 실제 저장소 기준 상대 경로를 기록한다. +4. 모든 `${lowercase_snake_case}` placeholder를 실제 값으로 교체한다. `${stable_section_id}`에는 고유한 소문자 케밥 표기법을 사용한다. 영역 이름이 바뀌어도 ID는 유지하며 삭제된 ID를 재사용하지 않는다. +5. 각 경로의 종류, 책임, 공개 진입점, 내부 구현 경계, 편집 정책 및 생성 주체를 기록한다. 행은 실제 경로 수만큼 복제한다. +6. 영역 간 의존마다 출발·대상 영역, 허용 여부, 규칙 및 공유 계약 위치를 기록한다. 역방향 또는 순환 의존은 명시적으로 금지한다. +7. 새 모듈과 파일의 배치, 이름·패키지 및 소스·테스트 대응 규칙을 기록한다. +8. 실제로 실행 가능한 구조 검사와 정적 분석 명령, 기대 결과 및 문서와 실제 트리의 정합성 확인 방법을 기록한다. + +경로는 대상 저장소 루트 기준으로 작성하고 근거 없이 영역이나 규칙을 추측하지 않는다. 해당 사항이 없으면 `없음`으로 기록한다. 완성 문서에는 placeholder나 작성 안내 문구를 남기지 않는다. diff --git a/harness/docs/project-structure/TEMPLATE.md b/harness/docs/project-structure/TEMPLATE.md new file mode 100644 index 0000000..013f58e --- /dev/null +++ b/harness/docs/project-structure/TEMPLATE.md @@ -0,0 +1,53 @@ +# ${project_structure_title} + +## 목적과 경계 + +${purpose} + +- 포함 경계: ${included_boundary} +- 제외 경계: ${excluded_boundary} + +## 디렉터리 구조 + +### ${stable_section_id} — ${area_name} + +```text +${directory_tree} +``` + +| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 | +|---|---|---|---|---|---|---| +| `${repository_relative_path}` | ${path_type} | ${responsibility} | ${public_entry_point_or_none} | ${internal_boundary} | ${edit_policy} | ${generator_or_none} | + +## 의존 방향 + +| 출발 영역 | 대상 영역 | 허용 여부 | 규칙 | 공유 계약 위치 | +|---|---|---|---|---| +| `${source_area_id}` | `${target_area_id}` | ${allowed_or_forbidden} | ${dependency_rule} | ${shared_contract_path_or_none} | + +## 변경 규칙 + +| 구분 | 규칙 | +|---|---| +| 새 모듈·파일 배치 | ${module_and_file_placement_rule} | +| 이름·패키지 | ${naming_and_package_rule} | +| 소스·테스트 대응 | ${source_test_mapping_rule} | +| 생성 산출물 | ${generated_artifact_rule} | + +## 검증 + +- 구조 검사 명령: + + ```text + ${structure_validation_command} + ``` + +- 구조 검사 기대 결과: ${structure_validation_expected_result} +- 정적 분석 명령: + + ```text + ${static_analysis_command} + ``` + +- 정적 분석 기대 결과: ${static_analysis_expected_result} +- 문서와 실제 트리의 정합성: ${document_tree_consistency_validation} diff --git a/harness/docs/requirements/README.md b/harness/docs/requirements/README.md new file mode 100644 index 0000000..35ae167 --- /dev/null +++ b/harness/docs/requirements/README.md @@ -0,0 +1,107 @@ +# 요구사항 + +각 요구사항 문서는 Markdown 본문 앞에 JSON 프런트 매터를 둔다. + +## 정본 + +요구사항 작성·검토에는 이 디렉터리의 `README.md`와 `TEMPLATE.md` 및 이와 byte parity가 검증된 trusted bundle만 정본으로 사용한다. 대상 저장소에 기존 `docs/requirements/README.md` 또는 `docs/requirements/TEMPLATE.md`가 있더라도 그 내용·schema·lifecycle·문서 구조를 작성 정책이나 템플릿으로 사용하지 않는다. 대상 저장소에서는 실제 `req-NNN_.md` 파일만 ID 충돌과 기존 정본 산출물 여부를 판정하기 위해 조사한다. + +## 저장 위치 + +완성된 요구사항명세서는 대상 저장소 루트 기준 다음 경로에 저장한다. + +```text +docs/requirements/req-NNN_.md +``` + +같은 `REQ-NNN`의 문서가 이미 존재하면 별도 파일을 만들지 않고 기존 정본 문서를 갱신한다. 이 디렉터리의 [`README.md`](README.md)와 [`TEMPLATE.md`](TEMPLATE.md)는 하네스의 작성 규칙과 템플릿이며 대상 저장소에 저장하는 산출물이 아니다. + +## 파일 이름 규칙 + +```text +req-NNN_.md +``` + +- `NNN`: 요구사항 ID를 나타내는 세 자리 십진수 +- ``: 요구사항 제목을 나타내는 소문자 케밥 표기법의 ASCII 슬러그 + +## 메타데이터 스키마 + +```json +{ + "schema": "gitea-agent.requirement/v1", + "id": "${requirement_id}", + "title": "${title}", + "revision": "1.0", + "status": "approved", + "depends_on": [], + "applies_to": ["${product_area}"], + "language": "ko" +} +``` + +- `id`: 저장소 안에서 고유한 `REQ-NNN` 형식의 요구사항 식별자. 파일 이름의 `NNN` 및 H1과 일치해야 한다. +- `revision`: 최초 작성 시 `1.0`으로 시작하며 요구사항의 의미가 변경될 때 올린다. +- `status`: 구현 에이전트가 승인된 Discovery를 근거로 작성하므로 항상 `approved`로 기록한다. 다른 값은 허용하지 않는다. +- `depends_on`: 선행 요구사항 식별자 배열. 선행 요구사항이 없으면 빈 배열을 사용한다. +- `applies_to`: 요구사항이 적용되는 제품 영역 식별자 배열. 하나 이상의 값을 기록한다. + +### 메타데이터 배열 규칙 + +- 배열 값은 중복 없이 안정적인 순서로 기록한다. +- `depends_on` 값은 같은 저장소의 유효한 `REQ-NNN`만 허용하며 자기 자신과 직접·간접 순환 참조를 금지한다. +- `applies_to` 값은 소문자 케밥 표기법의 제품 영역 식별자를 사용한다. + +## Placeholder와 반복 블록 + +- 교체 대상은 `${lowercase_snake_case}` 형식만 사용한다. +- TEMPLATE의 모든 `${...}` placeholder를 실제 값으로 교체하며 완성 문서에 하나도 남기지 않는다. +- `## 5. 요구사항`의 H3 블록은 독립 요구사항 수만큼 복제하고 `REQ-NNN.01`부터 연속 식별자를 부여한다. +- `## 7. 인수 기준`의 H3 블록은 각 요구사항에 하나 이상 두고 `REQ-NNN.XX-AC01`부터 요구사항별로 연속 식별자를 부여한다. +- 사용하지 않는 예시 블록이나 작성 안내 문구를 완성 문서에 남기지 않는다. + +## 완성 문서 검증 + +- 파일 이름의 `NNN`, 프런트 매터의 `id`, H1의 요구사항 식별자가 일치해야 한다. +- 요구사항과 인수 기준 식별자는 각각 `01`부터 빠짐없이 연속되어야 한다. +- 모든 필수 섹션을 채우고 해당 사항이 없으면 `없음`으로 기록한다. +- 프런트 매터는 유효한 JSON이어야 하며 `schema`, `status`, `language`는 정해진 값을 정확히 사용해야 한다. + +## 제약과 불변조건 작성 방법 + +구현 방식과 무관하게 유지해야 하는 데이터·상태·보안·원자성·정밀도·동시성·순서 조건을 기록한다. 하위 호환성은 이 섹션에 중복하지 않는다. + +## 하위 호환성 작성 방법 + +문서 전체에 적용되는 공통 하위 호환 계약을 기록한다. + +- 유지해야 하는 기존 API, 설정, 데이터, 사용자 흐름을 명시한다. +- 의도적으로 변경되는 동작은 적용 경계와 마이그레이션 방법을 함께 기록한다. +- 개별 요구사항에만 해당하는 내용은 해당 요구사항의 `영향 범위 > 호환성`에 기록한다. +- 기존 계약에 영향이 없으면 `없음`으로 기록한다. + +## 추적성 작성 방법 + +요구사항명세서는 구현 에이전트가 승인된 Discovery를 근거로 초기 구현과 함께 작성한다. + +- 같은 저장소의 근거 이슈는 `#NNN` 형식으로 기록한다. +- 초기 작성 시점에는 구현 PR이 아직 없으므로 `구현 PR`은 `미정`으로 기록하며 번호를 추측하지 않는다. +- 다른 저장소의 Issue 또는 PR을 참조할 때는 `owner/repository#NNN` 형식으로 저장소를 명시한다. +- 관련 설계 문서는 저장소 기준 상대 경로로 기록한다. +- 관련 요구사항은 `REQ-NNN` 식별자로 기록한다. +- 여러 값은 쉼표로 구분하고, 해당 항목이 없으면 `없음`으로 기록한다. + +## Python 파싱 + +프런트 매터 본문은 JSON이므로 별도 의존성 없이 Python 표준 라이브러리로 읽을 수 있다. + +```python +import json +from pathlib import Path + +text = Path("docs/requirements/req-001_algorithm-input-contract.md").read_text(encoding="utf-8") +_, metadata_json, _ = text.split("---", 2) +metadata = json.loads(metadata_json) +``` + +JSON 프런트 매터의 `revision`과 `status`가 문서의 기준값이다. diff --git a/harness/docs/requirements/TEMPLATE.md b/harness/docs/requirements/TEMPLATE.md new file mode 100644 index 0000000..f22d148 --- /dev/null +++ b/harness/docs/requirements/TEMPLATE.md @@ -0,0 +1,96 @@ +--- +{ + "schema": "gitea-agent.requirement/v1", + "id": "${requirement_id}", + "title": "${title}", + "revision": "1.0", + "status": "approved", + "depends_on": [], + "applies_to": ["${product_area}"], + "language": "ko" +} +--- + +# ${requirement_id} — ${title} + +## 1. 목적 + +${purpose} + +## 2. 배경 + +${background} + +## 3. 범위 + +### 포함 + +${in_scope} + +### 제외 + +${out_of_scope} + +## 4. 용어와 기준 + +${terms_and_authorities} + +## 5. 요구사항 + +### ${requirement_item_id} — ${requirement_item_title} + +${requirement_statement} + +**영향 범위** + +- 인터페이스: ${interfaces_or_none} +- 영속성: ${persistence_or_none} +- 후속 사용 주체: ${downstream_consumers_or_none} +- 마이그레이션: ${migration_or_none} +- 호환성: ${compatibility_or_none} +- 배포 전제 조건: ${deployment_prerequisites_or_none} + +## 6. 제약과 불변조건 + +${constraints_and_invariants} + +## 7. 인수 기준 + +### ${acceptance_criterion_id} — ${acceptance_criterion_title} + +**판정 기준** + +${judgment} + +**검증** + +${verification} + +**검증 분류** + +- 수준: ${verification_level} +- 백엔드: ${verification_backend_or_none} +- 케이스: ${verification_cases} + +**상태 불변조건** + +${state_invariants} + +## 8. 오류 및 충돌 계약 + +${error_and_conflict_contract} + +## 9. 데이터 및 인터페이스 계약 + +${data_and_interface_contract} + +## 10. 하위 호환성 + +${backward_compatibility} + +## 11. 추적성 + +- 근거 이슈: ${source_issue_id} +- 구현 PR: 미정 +- 관련 설계 문서: ${related_design_documents_or_none} +- 관련 요구사항: ${related_requirements_or_none} diff --git a/harness/docs/runtime-flow/README.md b/harness/docs/runtime-flow/README.md new file mode 100644 index 0000000..13b529a --- /dev/null +++ b/harness/docs/runtime-flow/README.md @@ -0,0 +1,23 @@ +# 런타임 흐름 작성 방법 + +런타임 흐름 문서는 승인된 Discovery와 구현 결과를 근거로 시작 이벤트부터 종료 상태까지의 실행 단계, 상태 변경 및 실패 복구 계약을 설명한다. + +## 저장 위치 + +완성된 런타임 흐름 문서는 대상 저장소 루트 기준 `docs/runtime-flow.md`에 저장한다. 파일이 이미 존재하면 별도 문서를 만들지 않고 이 정본 문서를 갱신한다. + +이 디렉터리의 [`README.md`](README.md)와 [`TEMPLATE.md`](TEMPLATE.md)는 하네스의 작성 규칙과 템플릿이며 대상 저장소에 저장하는 산출물이 아니다. + +## 작성 방법 + +1. [`TEMPLATE.md`](TEMPLATE.md)를 복사하고 섹션 순서를 유지한다. +2. 목적, 포함·제외 범위 및 흐름의 시작·종료 조건을 확정한다. +3. `흐름`의 H3 블록을 독립 실행 흐름 수만큼 복제하고 단계 행을 실제 실행 순서대로 작성한다. +4. 모든 `${lowercase_snake_case}` placeholder를 실제 값으로 교체한다. `${stable_section_id}`에는 고유한 소문자 케밥 표기법을 사용한다. 흐름 이름이 바뀌어도 ID는 유지하며 삭제된 ID를 재사용하지 않는다. +5. 각 단계에 단계 ID, 실행 주체, 시작 조건·입력, 처리, 읽기·쓰기 상태, 외부 호출, 성공 시 다음 단계 및 실패 시 전이를 기록한다. +6. 각 실패에 추가 쓰기 중단 여부, 재시도, 중복 방지, 보상·수동 복구 및 최종 상태를 기록한다. +7. 동시 실행, 잠금·직렬화 단위, 순서 보장 및 충돌 처리를 기록한다. +8. 로그·지표·추적 신호와 correlation ID, 진단 속성 및 민감정보 제거 규칙을 기록한다. +9. 정상·실패·경계·동시성 흐름을 실제로 검증할 방법과 운영 진입점의 기대 결과를 기록한다. + +단계 ID는 문서 안에서 고유해야 하며 모든 다음 단계와 실패 전이는 실제 단계 또는 명시된 종료 상태를 가리켜야 한다. 근거 없이 흐름이나 동작을 추측하지 않는다. 해당 사항이 없으면 `없음`으로 기록한다. 완성 문서에는 placeholder나 작성 안내 문구를 남기지 않는다. diff --git a/harness/docs/runtime-flow/TEMPLATE.md b/harness/docs/runtime-flow/TEMPLATE.md new file mode 100644 index 0000000..8416187 --- /dev/null +++ b/harness/docs/runtime-flow/TEMPLATE.md @@ -0,0 +1,51 @@ +# ${runtime_flow_title} + +## 목적과 범위 + +${purpose} + +- 포함 범위: ${included_scope} +- 제외 범위: ${excluded_scope} +- 시작 조건: ${start_condition} +- 성공 종료 상태: ${success_terminal_state} +- 실패 종료 상태: ${failure_terminal_state} + +## 흐름 + +### ${stable_section_id} — ${flow_name} + +- 시작 이벤트: ${trigger_event} +- 선행조건: ${preconditions} +- 실행 방식: ${synchronous_or_asynchronous} +- 트랜잭션·영속성 경계: ${transaction_and_persistence_boundary} + +| 단계 ID | 실행 주체 | 시작 조건·입력 | 처리 | 읽기·쓰기 상태 | 외부 호출 | 성공 시 다음 단계 | 실패 시 전이 | +|---|---|---|---|---|---|---|---| +| `${step_id}` | ${actor_or_component} | ${step_condition_and_input} | ${processing} | ${state_reads_and_writes} | ${external_call_or_none} | `${success_next_step_or_terminal}` | `${failure_transition_or_terminal}` | + +## 실패와 복구 + +| 대상 단계 | 실패 조건 | 추가 쓰기 중단 | 재시도 계약 | 중복 방지 | 보상·수동 복구 | 최종 상태 | +|---|---|---|---|---|---|---| +| `${failed_step_id}` | ${failure_condition} | ${stop_further_writes} | ${retry_contract} | ${idempotency_contract} | ${compensation_or_manual_recovery} | `${failure_final_state}` | + +### 동시성과 순서 + +- 동시 실행 허용 여부: ${concurrent_execution_policy} +- 잠금·직렬화 단위: ${locking_or_serialization_scope} +- 순서 보장: ${ordering_guarantee} +- 충돌 검출·처리: ${conflict_detection_and_handling} + +## 관찰 가능성 + +| 신호 종류 | 이름 | 발생 단계·조건 | 상관 식별자 | 진단 속성 | 민감정보 처리 | +|---|---|---|---|---|---| +| ${signal_type} | `${signal_name}` | ${signal_step_and_condition} | ${correlation_identifier} | ${diagnostic_attributes} | ${sensitive_data_handling} | + +## 검증 + +- 정상 흐름: ${success_flow_validation} +- 실패 흐름: ${failure_flow_validation} +- 경계 흐름: ${boundary_flow_validation} +- 동시성 흐름: ${concurrency_flow_validation} +- 운영 진입점과 기대 결과: ${operational_entry_point_and_expected_result} diff --git a/harness/operations/repository-cache.md b/harness/operations/repository-cache.md new file mode 100644 index 0000000..c40e264 --- /dev/null +++ b/harness/operations/repository-cache.md @@ -0,0 +1,248 @@ +# Repository cache 운영 지침 + +이 문서는 Gitea Agent harness가 사용하는 local repository cache의 canonical 운영 규칙이다. 모든 agent와 skill은 cache를 생성·갱신·검증할 때 이 문서를 읽고 따라야 하며 별도의 완화된 규칙을 만들지 않는다. + +## 목적과 범위 + +Repository cache는 Gitea repository의 Git 객체와 refs를 재사용하기 위한 bare mirror다. 문서 근거 조사와 Issue별 worktree 생성의 source로 사용할 수 있지만 그 자체는 checkout, 작업 공간 또는 외부 상태의 정본이 아니다. + +- Gitea MCP readback이 repository identity와 authoritative ref의 정본이다. +- `repository.git`은 확인한 Gitea 상태와 일치할 때만 사용한다. +- cache 생성과 갱신은 local filesystem 및 Git refs만 변경한다. Gitea에는 write하지 않는다. + +## 경로 구조 + +```text +/opt/data/gitea-agent/repositories/ +├── .locks/ +│ └── / +│ └── .bootstrap.lock +└── / + └── / + ├── repository.git + ├── locks/ + │ ├── repository.lock + │ └── issue-.lock + └── worktrees/ + └── issue-/ + ├── state.json + └── checkout/ +``` + +- `.locks//.bootstrap.lock`은 cache가 아직 없을 때도 사용할 수 있는 초기화 mutex다. +- `repository.lock`은 clone, fetch와 ref 검증을 직렬화한다. +- `issue-.lock`은 같은 Issue를 대상으로 하는 worker와 외부 write workflow를 직렬화한다. 필요할 때 생성한다. +- `repository.git`은 `git clone --mirror`로 만든 bare mirror다. +- `worktrees/issue-/checkout`은 해당 Issue의 유일한 linked worktree다. `state.json`은 checkout 밖에서 worktree identity와 생성 근거를 보존한다. +- checkout과 worktree metadata는 위 canonical 위치에만 생성한다. 다른 checkout, 임시 clone 또는 repository 밖의 workspace를 구현 근거로 사용하지 않는다. + +## 식별자와 경로 안전성 + +1. `owner`와 `repository`는 요청에서 받은 값을 보존하되 각각 안전한 단일 경로 요소인지 검증한다. +2. 빈 값, 경로 구분자, NUL, `.`과 `..` 또는 traversal을 허용하지 않는다. +3. `/opt/data/gitea-agent`는 지정된 trusted harness root다. NFS mount root의 numeric owner가 실행 UID와 달라도 허용하지만 exact path가 directory이고 symlink가 아니며 world-writable이 아닌지 확인한다. +4. trust boundary 아래 `repositories`, `.locks`, owner, repository와 `locks`는 생성 전후 `lstat`으로 검사한다. 모두 symlink가 아닌 directory, 실행 role 소유, mode `0700`이어야 한다. +5. lock 파일과 임시 credential helper는 실행 role 소유와 mode `0600`을 요구한다. +6. `worktrees`, `issue-`와 `checkout`은 symlink가 아닌 directory이고 실행 role 소유여야 한다. `worktrees`와 Issue directory는 mode `0700`, `state.json`은 mode `0600`이어야 하며 checkout과 그 상위 경로는 group 또는 world-writable이면 안 된다. +7. 기존 path가 malformed 상태면 삭제, chmod, chown, reset 또는 덮어쓰기로 자동 복구하지 않고 fail-closed한다. 권한 변경은 운영자가 exact path와 mode를 명시적으로 승인한 경우에만 수행하고 즉시 재검증한다. + +## Lock 규칙 + +- Linux OS advisory lock을 사용한다. lock file descriptor는 symlink를 따라가지 않는 방식으로 열고 원자적으로 lock을 획득한다. +- 독립적인 신규 cache 초기화 순서는 `bootstrap.lock → repository.lock`이다. +- Issue workflow가 기존 cache를 사용할 때의 순서는 `issue-.lock → repository.lock`이다. +- Issue workflow가 cache 초기화까지 수행할 때의 순서는 `issue-.lock → bootstrap.lock → repository.lock`이다. bootstrap 절차는 이미 획득한 Issue lock을 해제하거나 다시 획득하지 않는다. +- Worktree 생성, 등록, lock, 이동, 제거 또는 repair는 Issue lock을 유지한 채 repository lock을 추가로 획득해서 수행한다. 일반 파일 편집, 테스트와 commit에는 Issue lock만 유지하고 repository lock은 보유하지 않는다. +- 같은 repository의 다른 Issue worktree를 검사, 변경 또는 제거하지 않는다. 한 Issue workflow는 자신의 exact Issue worktree만 다룬다. +- 반대 순서로 lock을 획득하거나 이미 잡은 상위 lock을 해제한 뒤 하위 작업을 계속하지 않는다. +- 정상 종료, 오류와 중단 경로 모두에서 descriptor를 닫아 lock을 해제한다. +- lock 파일은 unlink하지 않는다. 프로세스 종료 시 kernel이 advisory lock을 해제하므로 남아 있는 파일을 stale active lock으로 간주하거나 PID 기준으로 삭제하지 않는다. + +## 신규 cache 초기화 + +1. Gitea MCP `get_me`로 허용된 role identity를 확인한다. +2. exact `owner/repository` metadata, clone URL, default branch와 branch SHA를 Gitea에서 읽는다. +3. bootstrap lock을 획득한 뒤 target을 다시 검사한다. 이미 유효한 cache가 있으면 새로 만들지 않고 기존 cache 검증 절차로 전환한다. +4. trusted parent 아래 같은 filesystem에 run-unique sibling staging directory를 mode `0700`으로 만든다. +5. staging에 `locks/repository.lock`을 mode `0600`으로 만들고 repository lock을 획득한다. bootstrap lock과 repository lock을 모두 유지한 상태에서 clone, ref 검증과 publication을 수행한다. +6. credential이 필요하면 role profile의 credential을 short-lived child process로만 전달한다. credential을 URL, argv, Git config, log 또는 remote URL에 저장하지 않는다. +7. staging 안에 `git clone --mirror`로 `repository.git`을 만든다. +8. staging mirror가 bare repository인지, `origin`이 exact owner/repository인지, default branch ref가 Gitea SHA와 일치하는지 확인한다. +9. staging 내용과 parent directory를 flush한 뒤 no-replace atomic rename으로 target directory를 게시한다. NFS가 no-replace primitive를 지원하지 않는 경우에만, bootstrap lock을 유지하고 parent가 실행 role 소유의 mode `0700`이며 target이 즉시 `lstat`에서 부재함을 확인한 뒤 동일 filesystem의 atomic rename을 사용할 수 있다. target이 존재하거나 조건을 증명할 수 없으면 fallback하지 않는다. +10. 게시 직후 target의 path 유형, 소유권, 권한, bare 상태, remote identity, default branch와 SHA를 다시 검증한다. +11. 모든 검증이 끝난 후 repository lock과 bootstrap lock을 역순으로 해제한다. + +초기화 실패 시 현재 run이 만든 staging만 안전하게 제거할 수 있다. final target, 다른 run의 staging 또는 정체를 확인할 수 없는 잔여 경로는 자동 삭제하지 않는다. + +## 기존 cache 갱신 + +1. 필요한 상위 lock을 유지한 채 repository lock을 획득한다. +2. target과 `repository.git`의 path, ownership, permissions와 bare 상태를 다시 검증한다. +3. `origin`의 canonical owner/repository identity가 요청과 일치하는지 확인한다. URL 문자열의 credential이나 transport hostname만으로 identity를 판정하지 않는다. +4. 명시적으로 검증한 origin에서 mirror refs를 prune/fetch한다. 임의 remote 교체, force reset 또는 다른 repository 재사용은 금지한다. +5. Gitea에서 default branch와 SHA를 다시 읽고 local ref와 일치하는지 확인한다. +6. 일치한 exact SHA를 이번 작업의 pinned source revision으로 기록한다. +7. repository lock을 해제한다. + +## Issue worktree 모델 + +- 한 repository의 한 Issue에는 persistent linked worktree 하나만 허용한다. worktree는 구현 결과가 안전하게 전달되거나 명시적으로 폐기될 때까지 세션 간 보존한다. +- Worktree는 `repository.git`의 exact commit에서 detached HEAD로 생성한다. moving branch, tag, abbreviated SHA 또는 `HEAD`를 생성 기준으로 사용하지 않는다. +- Mirror의 refspec은 `refs/*`를 직접 mirror하므로 구현용 local branch를 `repository.git`의 `refs/heads/*`에 만들지 않는다. namespace를 붙인 local branch도 허용하지 않는다. +- Detached worktree에서 commit할 수 있다. 해당 commit은 worktree HEAD가 가리키는 동안 reachable 상태로 보존한다. +- 구현 요청은 local 편집, 테스트와 detached commit을 승인할 수 있지만 remote branch 생성, push, PR, Issue 변경 또는 merge를 자동 승인하지 않는다. 외부 write는 별도의 명시적 승인과 readback 계약을 따른다. +- `git worktree prune`, 전체 worktree repair 또는 다른 Issue에 영향을 줄 수 있는 일괄 cleanup을 자동 실행하지 않는다. + +## Worktree state 계약 + +`state.json`은 checkout 안에 넣거나 repository source로 commit하지 않는다. credential, token, clone credential URL, 환경변수 값, PID 또는 임시 credential helper 경로를 기록하지 않는다. 최소 형식은 다음과 같다. + +```json +{ + "schema": "gitea-agent.worktree/v1", + "owner": "", + "repository": "", + "issue": 1, + "base_sha": "", + "issue_updated_at": "", + "issue_body_sha256": "", + "created_by_role": "", + "mode": "detached" +} +``` + +- `base_sha`는 40자리 full commit SHA이고 Gitea 또는 승인된 Discovery에서 확인한 구현 기준 revision이어야 한다. +- `issue_body_sha256`은 구현 승인의 근거가 된 authoritative Issue body의 exact UTF-8 bytes에 대해 계산한다. +- `created_by_role`은 credential 값이 아닌 harness role identity다. PID나 현재 process 존재 여부를 worktree 소유권 또는 활성 상태의 정본으로 사용하지 않는다. +- 파일은 run-unique sibling temporary file에 mode `0600`으로 완성하고 flush한 뒤 no-replace atomic rename으로 게시한다. 기존 `state.json`을 덮어쓰지 않는다. + +## Worktree admission gate + +Worktree를 신규 생성하거나 기존 worktree에서 구현을 재개하기 전에 다음을 모두 확인한다. + +1. exact `owner`, `repository`와 Issue 번호가 검증됐고 Issue lock을 획득했다. +2. Gitea MCP로 authenticated role, exact repository metadata, Issue body, `updated_at`과 구현 승인 상태를 읽었다. +3. repository cache가 이 문서의 path, ownership, permission, bare, remote identity와 authoritative ref 검증을 통과했다. +4. 구현에 사용할 exact base SHA와 그 출처를 결정했다. Discovery pinned revision과 현재 default branch SHA가 다르면 어느 한쪽을 자동 선택하지 않고 변경을 조사해 요구사항과 충돌하지 않는 base를 확정하거나 중단한다. +5. `repository.git`에 exact base SHA의 commit object가 존재한다. 문자열 형식이나 object 존재만으로 Gitea 또는 승인된 Discovery에서 유래했다는 검증을 대체하지 않는다. +6. canonical target path, `state.json`과 Git worktree registration의 존재 여부를 함께 조사했다. +7. 신규 생성이면 target path와 동일 Issue registration이 모두 없어야 한다. 재사용이면 아래의 기존 worktree 검증을 모두 통과해야 한다. + +Gate가 끝난 뒤 write 직전에 Issue body, `updated_at`과 구현 승인 상태를 다시 읽는다. admission snapshot과 다르면 worktree를 생성하거나 기존 worktree를 수정하지 않는다. + +## Worktree 신규 생성 + +1. Issue lock을 유지한 채 repository lock을 획득한다. +2. cache와 base commit을 다시 검증하고 `worktrees`와 Issue directory를 canonical mode로 생성한 뒤 즉시 `lstat`한다. +3. final `checkout`, `state.json`과 동일 Issue의 Git worktree registration이 모두 없는지 다시 확인한다. +4. `git --git-dir= worktree add --detach `와 동등한 호출로 exact final checkout을 생성한다. branch 생성 option을 사용하지 않는다. +5. worktree를 Issue 용도로 `git worktree lock`하고 reason에 repository credential이나 민감정보 없이 Issue 번호를 기록한다. +6. `state.json`을 state 계약에 따라 원자적으로 게시한다. +7. 생성 직후 다음을 모두 검증한다. + - `git rev-parse HEAD`가 exact base SHA와 일치한다. + - `git symbolic-ref -q HEAD`가 성공하지 않아 detached HEAD임이 확인된다. + - `git status --porcelain`이 비어 있다. + - `git rev-parse --git-common-dir`가 canonical `repository.git`과 동일하다. + - `git worktree list --porcelain`에 exact checkout path가 한 번만 있고 locked 상태다. + - checkout path와 `state.json`의 identity, ownership과 permission이 canonical 계약을 충족한다. +8. 모든 검증 후 repository lock을 해제한다. Issue lock은 해당 Issue workflow가 끝날 때까지 유지한다. + +Git 명령의 성공 exit만으로 생성 완료를 판정하지 않는다. Git registration, checkout, state와 Issue snapshot을 모두 검증해야 한다. + +## 기존 worktree 재사용 + +기존 directory 또는 Git registration 중 하나라도 발견되면 신규 생성으로 덮어쓰지 않는다. Issue lock을 유지하고 repository lock 아래에서 다음을 모두 확인한다. + +- `state.json`이 유효한 `gitea-agent.worktree/v1`이고 owner, repository, Issue와 role이 현재 workflow와 일치한다. +- exact checkout이 canonical `repository.git`에 한 번만 등록돼 있고 locked 상태다. +- checkout의 common Git directory가 canonical `repository.git`이다. +- HEAD, index, tracked 변경과 untracked 파일을 모두 읽을 수 있다. +- HEAD가 `base_sha`와 같거나 `base_sha`가 HEAD의 ancestor다. ancestor가 아니면 history를 자동 재작성하지 않는다. +- merge, rebase, cherry-pick, revert, bisect 또는 다른 미완료 Git operation이 없다. +- 현재 Issue body와 `updated_at`을 state의 snapshot과 비교해 drift 여부를 판정했다. + +Dirty worktree, untracked 파일 또는 base 이후 commit은 진행 중 구현에서 정상일 수 있으므로 존재만으로 삭제하거나 초기화하지 않는다. `git reset --hard`, `git clean`, 자동 checkout, switch, rebase 또는 history rewrite를 수행하지 않는다. Issue가 변경됐다면 이전과 현재 snapshot, 기존 diff·commit과 acceptance criteria를 비교해 재사용 가능성이 명확할 때만 state를 별도 승인된 절차로 갱신하고, 판단할 수 없으면 fail-closed한다. + +## Worktree 작업과 delivery + +- 일반 편집, 테스트와 commit은 exact Issue lock 아래에서만 수행한다. 현재 worktree 외의 source, 다른 checkout 또는 installed package를 수정하거나 구현 근거로 사용하지 않는다. +- commit 전후 base SHA, HEAD SHA, staged·unstaged·untracked 상태를 기록한다. detached 상태를 local branch로 자동 전환하지 않는다. +- 명시적으로 승인된 push는 source와 destination을 모두 지정해 `HEAD:refs/heads/` 형태와 동등하게 수행한다. destination 없는 push, force push와 mirror push는 금지한다. +- Push 후 Gitea에서 exact remote branch SHA를 다시 읽어 local HEAD와 일치하는 경우에만 전달 완료로 판정한다. PR 생성, merge, Issue comment·label·state 변경은 각각 별도 승인이 필요하다. +- 테스트 성공, local commit 또는 agent session 종료만으로 worktree를 제거하지 않는다. + +## Worktree 실패 처리와 제거 + +현재 run이 worktree를 생성했고 편집이나 commit이 시작되지 않았으며 worktree가 clean하고 HEAD가 base SHA와 같다는 것을 증명할 때만 생성 실패를 자동 cleanup할 수 있다. 이때 Issue lock과 repository lock을 모두 유지하고 worktree lock을 해제한 뒤 `git worktree remove`와 동등한 Git operation으로 exact registration과 checkout을 제거한다. filesystem 삭제를 먼저 수행하지 않는다. + +다음 상태는 자동 cleanup하지 않고 보존한 채 partial 또는 fail-closed로 보고한다. + +- dirty 또는 untracked 상태 +- base 이후 commit 존재 +- 생성 주체, identity 또는 base를 확인할 수 없음 +- `state.json`, checkout path와 Git registration 불일치 +- symlink, ownership 또는 permission 이상 +- 미완료 Git operation +- unlock, remove 또는 cleanup 결과가 불확실함 + +정상 worktree 제거는 사용자가 exact worktree 폐기를 승인했거나, 승인된 delivery가 완료되어 remote branch SHA가 local HEAD와 일치함을 authoritative readback한 경우에만 수행한다. 제거 전 base SHA, final HEAD SHA, dirty·untracked 상태, 미전달 commit과 remote 보존 여부를 확인하고 보고한다. Dirty 상태, 미전달 commit 또는 판정 불가능한 파일이 있으면 `--force`, filesystem 재귀 삭제, reset 또는 clean으로 우회하지 않는다. + +제거할 때는 Issue lock과 repository lock 아래에서 worktree lock을 해제하고 Git operation으로 checkout과 registration을 먼저 제거한다. 두 대상의 부재를 확인한 뒤 exact `state.json`과 비어 있는 Issue worktree directory만 제거하며, 현재 run이 만든 state temporary file이 있으면 identity를 확인한 뒤 함께 제거한다. 다른 파일이나 하위 directory가 남아 있으면 재귀 삭제하지 않고 중단한다. 마지막으로 exact checkout, Git registration, `state.json`과 Issue worktree directory가 모두 없는지 다시 검증한다. + +## Credential 안전 + +- credential 값은 role profile의 `.env`에만 저장하고 mode `0600`을 요구한다. +- worker에게 credential 값 또는 credential-bearing URL을 전달하지 않는다. +- Git 인증이 필요하면 short-lived askpass 또는 동등한 child-process mechanism을 사용하고 종료 시 제거한다. +- inherited credential, 다른 role의 credential 또는 interactive prompt로 fallback하지 않는다. +- 인증에 실패하면 public clone이나 MCP content read를 initialized mirror의 증거로 대체하지 않고 중단한다. +- Worktree source, state, Git config, reflog message, commit, patch, test artifact 또는 push refspec에 credential을 기록하지 않는다. + +## Fail-closed 조건 + +다음 중 하나라도 발생하면 cache를 사용하거나 자동 복구하지 않는다. + +- Gitea identity 또는 repository metadata를 확인할 수 없음 +- symlink, ownership, permission 또는 path type 불일치 +- non-bare 또는 손상된 `repository.git` +- remote repository identity 불일치 +- Gitea default branch SHA와 local ref 불일치 +- lock 획득 실패 또는 lock order 위반 +- credential 출처나 cleanup을 확인할 수 없음 +- 초기화·fetch 결과가 불확실함 + +Issue worktree workflow에서는 다음 조건도 fail-closed한다. Cache만 조사하는 workflow에서 worktree가 존재하지 않는 것은 오류가 아니다. + +- Issue identity, 구현 승인 또는 Issue snapshot drift를 판정할 수 없음 +- worktree path가 canonical 위치 밖이거나 symlink, ownership 또는 permission 계약을 위반함 +- target Issue의 worktree path 또는 Git registration이 존재하지만 `state.json`이 누락, 손상 또는 현재 owner, repository, Issue, role과 불일치함 +- worktree common Git directory가 exact `repository.git`과 다름 +- 동일 Issue에 여러 worktree가 등록됐거나 path와 registration이 불일치함 +- attached branch 상태이거나 mirror에 구현용 local branch가 생성됨 +- base SHA의 authoritative 출처 또는 commit object를 확인할 수 없음 +- 미완료 Git operation 또는 worktree cleanup 결과가 불확실함 + +## 검증 및 보고 + +완료 보고에는 다음을 포함한다. + +- authenticated Gitea identity +- exact `owner/repository` +- cache path와 신규 생성 또는 기존 재사용 여부 +- bare repository 검증 결과 +- canonical remote identity +- default branch와 Gitea/local SHA +- 획득한 lock 순서 +- credential 값이 남지 않았다는 확인 +- cache workflow의 partial 또는 fail-closed 상태 + +Issue worktree workflow의 완료 보고에는 다음도 포함한다. + +- Issue 번호, 승인 snapshot의 `updated_at`과 body digest +- worktree path, 신규 생성 또는 기존 재사용 여부와 locked·detached 검증 +- base SHA, final HEAD SHA와 base-to-HEAD ancestry +- staged, unstaged, untracked, 미완료 Git operation과 미전달 commit 상태 +- delivery가 승인됐다면 remote branch와 Gitea/local SHA readback +- partial 또는 fail-closed 상태 + +프로세스 exit와 clone/fetch 또는 worktree 명령의 성공 메시지만으로 완료를 주장하지 않는다. Cache workflow는 final cache path와 Gitea authoritative state를, Issue worktree workflow는 여기에 exact worktree registration, checkout, state와 Issue snapshot을 더해 모두 다시 읽은 경우에만 완료로 판정한다.