78 KiB
Repository cache 운영 지침
이 문서는 Gitea Agent harness가 사용하는 local repository cache의 canonical 운영 규칙이다. 모든 agent와 skill은 cache를 생성·갱신·검증할 때 이 문서와 Gitea authenticated access 운영 지침을 읽고 따라야 하며 별도의 완화된 규칙을 만들지 않는다.
목적과 범위
Repository cache는 Gitea repository의 Git 객체와 refs를 재사용하기 위한 bare mirror다. 문서 근거 조사와 Issue별 worktree 생성의 source로 사용할 수 있지만 그 자체는 checkout, 작업 공간 또는 외부 상태의 정본이 아니다.
- Canonical access 계약을 충족하는 authenticated Gitea API readback이 repository identity와 authoritative ref의 정본이다.
repository.git은 확인한 Gitea 상태와 일치할 때만 사용한다.- cache 생성과 갱신은 local filesystem 및 Git refs만 변경한다. Gitea에는 write하지 않는다.
Harness root 결정
Repository cache 경로는 고정된 machine-local 절대경로나 현재 작업 directory에서 추측하지 않는다. Agent가 이번 작업에서 read_file로 실제 읽은 이 문서의 경로가 exact <harness-root>/harness/operations/repository-cache.md 형식인지 확인하고, 그 경로에서 <harness-root>를 계산한다. 따라서 repository 전체를 다른 위치에 설치해도 cache root는 항상 <harness-root>/repositories다.
- 이 문서의 경로,
operations,harness와<harness-root>를 각각lstat하고 symlink가 아닌지 확인한다. - 문서 경로가 위 구조와 exact-match하지 않거나
<harness-root>를 하나로 결정할 수 없으면 fail-closed한다. pwd, shell 시작 directory, 사용자 home, 다른 checkout, 환경변수 또는 과거 session 값을 root 결정 근거로 사용하지 않는다.- Source checkout과 runtime cache가 같은 root 아래 있더라도 repository source로 혼동하지 않는다. Runtime cache는
<harness-root>/repositories아래에서만 관리한다.
경로 구조
<harness-root>/repositories/
├── .locks/
│ └── <owner>/
│ └── <repository>.bootstrap.lock
└── <owner>/
└── <repository>/
├── repository.git
├── locks/
│ ├── repository.lock
│ └── issue-<issue-number>.lock
└── worktrees/
└── issue-<issue-number>/
├── state.json
├── checkout/
└── verification/
└── <run-id>/
.locks/<owner>/<repository>.bootstrap.lock은 cache가 아직 없을 때도 사용할 수 있는 초기화 mutex다.repository.lock은 clone, fetch와 ref 검증을 직렬화한다.issue-<issue-number>.lock은 같은 Issue를 대상으로 하는 worker와 외부 write workflow를 직렬화한다. 필요할 때 생성한다.repository.git은git clone --mirror로 만든 bare mirror다.worktrees/issue-<issue-number>/checkout은 해당 Issue의 유일한 linked worktree다.state.json은 checkout 밖에서 worktree identity와 생성 근거를 보존한다.- checkout과 worktree metadata는 위 canonical 위치에만 생성한다. 다른 checkout, 임시 clone 또는 repository 밖의 workspace를 구현 근거로 사용하지 않는다.
식별자와 경로 안전성
owner와repository는 요청에서 받은 값을 보존하되 각각 안전한 단일 경로 요소인지 검증한다.- 빈 값, 경로 구분자, NUL,
.과..또는 traversal을 허용하지 않는다. - 위 규칙으로 계산하고 검증한
<harness-root>만 trusted harness root로 사용한다. Exact path가 symlink가 아닌 directory이고 world-writable이 아닌지 확인한다. NFS mount root의 numeric owner가 실행 UID와 달라도 허용한다. - 기존
repositories는lstat으로 검사해 symlink가 아닌 directory, 실행 role 소유이며 world-writable이 아님을 확인한다. Group-writable이면 directory의 numeric GID가 현재 system에서 해석되고, 해당 GID를 primary group으로 쓰는 계정과 group member 목록을 합친 effective member가 실행 role 하나뿐임을 확인해야 한다. Group identity나 전체 membership을 확인할 수 없거나 다른 계정이 포함되면 fail-closed한다. - 새
repositories를 만들 때는 실행 role 소유와 mode0700으로 생성한다. 기존 directory가 4번의 검증을 통과하면 mode0700,0750,0755,0770또는0775를 자동으로 chmod하지 않고 그대로 사용할 수 있다. - Trust boundary 아래
.locks, owner, repository와locks는 생성 전후lstat으로 검사한다. 모두 symlink가 아닌 directory, 실행 role 소유, mode0700이어야 한다. - Lock 파일과 임시 credential helper는 실행 role 소유와 mode
0600을 요구한다. worktrees,issue-<issue-number>와checkout은 symlink가 아닌 directory이고 실행 role 소유여야 한다.worktrees와 Issue directory는 mode0700,state.json은 mode0600이어야 하며 checkout과 그 상위 runtime 경로는 group 또는 world-writable이면 안 된다.- 기존 path가 malformed 상태면 삭제, chmod, chown, reset 또는 덮어쓰기로 자동 복구하지 않고 fail-closed한다. 권한 변경은 운영자가 exact path와 mode를 명시적으로 승인한 경우에만 수행하고 즉시 재검증한다.
Managed-lock broker (Issue workflow 필수)
Bootstrap 이후 모든 Issue workflow는 role-neutral shared managed-lock broker 하나를 사용한다. 개별 impl/spec/review skill이 직접 flock을 획득하거나 broker 규칙을 재정의하지 않는다. Broker executable은 harness/lib/managed_lock_broker.py이며 authenticated request action은 status, renew, acquire_repository, release_repository, release이고 최대 TTL은 3600초다.
- Broker runtime은
<harness-root>/repositories/<owner>/<repo>/worktrees/issue-<number>/runtime/<workflow-run-id>/에 둔다.<workflow-run-id>는 verification stage의<run-id>와 독립적이다. Runtime은 broker 소유 transient active resource이며verification/<run-id>는 보존되는 evidence다. - Runtime 디렉터리는 broker가 owner/repository/issue/role/workflow-run-id/TTL을 받아 만들고 serve-ready JSON의
handle과socket필드로 실제 경로를 반환한다. 호출자는 이 값을 각각HANDLE과SOCKET변수로 사용하며 임의 runtime directory, handle 또는 socket을 미리 만들지 않는다. Canonical basename은broker.handle과broker.sock이며 이미 제공한 handle/socket 옵션은 정확히 canonical 경로일 때만 허용된다. - runtime 생성은 승인이나 admission을 의미하지 않는다. Admission 전에 Issue lock을 획득하고, 필요한 trusted parent 생성만을 위한 짧은 own-repository lock을 획득한 다음 runtime을 만든다. Bootstrap은 Issue lease 아래 실행하지 않는다.
serve는 tracked background process로 시작하고 ready JSON을 기다린 뒤에만 Issue 작업을 시작한다. Ready 전에 broker는 bound filesystem socket node의 type·owner UID·device/inode와 exact mode0600을 검증한다. Client는 raw token을 read/log하지 않고 public request API가 인증을 캡슐화한다.
이 broker의 보안 경계에서 filesystem owner UID는 trusted principal이다. mode 0700/0600과 random bearer token은 다른 UID 또는 foreign token을 사용한 작업을 막지만, 같은 UID를 공유하는 상호 비신뢰 프로세스를 서로 격리하지는 못한다. 그런 격리가 필요한 배포는 각 role을 distinct UID로 실행해야 한다.
-
Issue lock은 broker의
serve가 관리한다. Repository lock이 필요하면 같은 handle로acquire_repository를 request하고, 끝나면release_repository를 request한다. 중첩 repository lock에 rawflock ... sleep또는 별도 lock holder를 사용하지 않는다. -
TTL 만료 전에
renew를 request한다. Broker crash 뒤 kernel이 advisory lock을 해제해도 stale handle은 진단 정보일 뿐이며probe가 authoritative하다. -
정상, 오류와 cancel 경로는 동일한 cleanup을 수행한다: 보유 중이면
release_repository,release, broker process wait/reap, 그리고 Issueprobe를 실행해free=true를 요구한다. 다른 Issue workflow가 합법적으로 repository lock을 먼저 획득할 수 있으므로 완료 gate는 Issue resource를 검사하고, 종료한 broker process가 자신의 repository FD를 더 이상 보유하지 않음을 wait/reap으로 증명한다. 이 순서를 검증하기 전에는 completed/log/report를 주장하지 않는다. Lock 파일은 절대 unlink하지 않는다. -
소켓 정리는 부모 directory FD를 기준으로 기록한 filesystem device/inode·type·owner가 일치하는 경로만 삭제한다. 경로가 다른 객체나 symlink로 교체되면 그 객체는 삭제하지 않고
socket-identity-changed로 보고한다. 같은 UID의 경쟁 프로세스까지 원자적 격리를 보장하지 않으며, SIGKILL/crash 뒤 소켓 삭제를 보장하지 않는다. Stale 경로를 자동 삭제하거나 재사용하지 않는다. 첫 소켓 정리 실패 뒤에는 부모 FD를 닫고 경로를 다시 열어 자동 재시도하지 않는다. 잔여 경로의 제거는 운영자가 exact 경로와 소유권을 확인하는 별도 복구이며, 반복 cleanup은 이전 부분 실패를 보존한다. -
정상 release 응답은 기존
{"status":"released"}를 유지한다. 정리가 일부 실패하면 broker는{"error":"cleanup-incomplete","cleanup":...}를 응답하고 종료하며,cleanup의lease_released,socket_removed,handle_saved,errors로 해제와 잔여 정리를 구분한다. Handle 기록 가능 시 같은 결과를 비밀값 없이 저장하고 stderr에도 오류 코드만 보고한다. 부분 실패는 nonzero process exit로 보고하며, 성공한 cleanup을 재적용하거나 실패 이력을 성공으로 덮어쓰지 않는다. 성공 기록을 실패로 덮어쓰지 않는다. 이 오류만으로 이미 해제한 lease를 재해제하거나 workflow 외부 write를 반복하지 않는다. -
완료 gate는 기존 release → wait/reap → Issue probe 순서에 더해 broker의 정상 종료와 run-owned 소켓 경로 부재를 확인한다.
probe free=true는 lock 해제 증거이지 소켓 권한·삭제 증거가 아니다. Lease 해제와 소켓·handle 정리 실패는 별도로 보고하고 완료로 간주하지 않는다.
Canonical command templates
# Runtime paths come only from broker ready JSON; never create them or read the token.
python3 <harness-root>/harness/lib/managed_lock_broker.py serve \
--root <harness-root> \
--owner <owner> --repository <repository> --issue <issue-number> \
--role <role> --run-id <workflow-run-id> \
--ttl-seconds <ttl-seconds>
# Parse only public paths from ready JSON; do not print token-bearing fields.
HANDLE=<ready-json-HANDLE>
SOCKET=<ready-json-SOCKET>
python3 <harness-root>/harness/lib/managed_lock_broker.py request \
--handle "$HANDLE" --action status
python3 <harness-root>/harness/lib/managed_lock_broker.py request \
--handle "$HANDLE" --action acquire_repository
python3 <harness-root>/harness/lib/managed_lock_broker.py request \
--handle "$HANDLE" --action release_repository
python3 <harness-root>/harness/lib/managed_lock_broker.py request \
--handle "$HANDLE" --action renew --ttl-seconds <ttl-seconds>
python3 <harness-root>/harness/lib/managed_lock_broker.py request \
--handle "$HANDLE" --action release
# After wait/reap of tracked broker; free=true mandatory for Issue resource.
python3 <harness-root>/harness/lib/managed_lock_broker.py probe \
--root <harness-root> \
--owner <owner> --repository <repository> --issue <issue-number> \
--resource issue
Lock 규칙
- Linux OS advisory lock을 사용한다. lock file descriptor는 symlink를 따라가지 않는 방식으로 열고 원자적으로 lock을 획득한다.
- 독립적인 신규 cache 초기화 순서는
bootstrap.lock → repository.lock이다. - Issue workflow가 기존 cache를 사용할 때 broker가 Issue lease를 보유하고, 같은 broker handle의
acquire_repositoryrequest로 repository lease를 중첩한다. 개념적 순서는Issue(broker) → repository(broker)이며 client는 raw descriptor lock을 사용하지 않는다. - Issue 작업에 사용할 cache가 없으면 Issue broker lease 획득 전에 신규 cache 초기화 절차를 완료한다. 초기화 중에는 Issue를 변경하지 않는다. Bootstrap의 repository lock과 bootstrap lock을 모두 해제한 뒤 broker를 시작하고 Issue lease를 획득하며, 이후에는 broker의
Issue → acquire_repository순서를 따른다. Issue 작업의 기준 snapshot은 broker Issue lease 획득 후 읽는다. - Worktree 생성, 등록, lock, 이동, 제거 또는 repair는 broker Issue lease를 유지한 채 같은 broker handle의
acquire_repositoryrequest로 repository lease를 추가 획득해서 수행한다. 일반 파일 편집, 테스트와 commit에는 broker Issue lease만 유지하고 repository lease는 보유하지 않는다. - 같은 repository의 다른 Issue worktree를 검사, 변경 또는 제거하지 않는다. 한 Issue workflow는 자신의 exact Issue worktree만 다룬다.
- 반대 순서로 lock을 획득하거나 이미 잡은 상위 lock을 해제한 뒤 하위 작업을 계속하지 않는다.
- 정상 종료, 오류와 중단 경로 모두에서 broker cleanup protocol을 수행한다: repository lease release, Issue lease release, broker wait/reap, Issue resource
probe --resource issue의free=truereadback. 다른 Issue가 그 사이 repository lock을 획득할 수 있으므로 repository의 순간적 busy 상태만으로 현재 broker cleanup 실패로 판정하지 않는다. - lock 파일은 unlink하지 않는다. 프로세스 종료 시 kernel이 advisory lock을 해제하므로 남아 있는 파일을 stale active lock으로 간주하거나 PID 기준으로 삭제하지 않는다.
신규 cache 초기화
- Authenticated Gitea API adapter로 허용된 role의 stable account identity를 확인한다. MCP면
get_me, 직접 REST면GET /api/v1/user를 사용한다. - exact
owner/repositorymetadata, clone URL, default branch와 branch SHA를 Gitea에서 읽는다. - bootstrap lock을 획득한 뒤 target을 다시 검사한다. 이미 유효한 cache가 있으면 새로 만들지 않고 기존 cache 검증 절차로 전환한다.
- trusted parent 아래 같은 filesystem에 run-unique sibling staging directory를 mode
0700으로 만든다. - staging에
locks/repository.lock을 mode0600으로 만들고 repository lock을 획득한다. bootstrap lock과 repository lock을 모두 유지한 상태에서 clone, ref 검증과 publication을 수행한다. - credential이 필요하면 role profile의 credential을 short-lived child process로만 전달한다. credential을 URL, argv, Git config, log 또는 remote URL에 저장하지 않는다.
- staging 안에
git clone --mirror로repository.git을 만든다. - staging mirror가 bare repository인지,
origin이 exact owner/repository인지, default branch ref가 Gitea SHA와 일치하는지 확인한다. - 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하지 않는다. - 게시 직후 target의 path 유형, 소유권, 권한, bare 상태, remote identity, default branch와 SHA를 다시 검증한다.
- 모든 검증이 끝난 후 repository lock과 bootstrap lock을 역순으로 해제한다.
초기화 실패 시 현재 run이 만든 staging만 안전하게 제거할 수 있다. final target, 다른 run의 staging 또는 정체를 확인할 수 없는 잔여 경로는 자동 삭제하지 않는다.
기존 cache 갱신
- Issue workflow는 broker Issue lease를 유지한 채 같은 handle에
acquire_repository를 request한다. 별도 승인된 non-Issue workflow는 해당 workflow에 명시된 상위 lock과 repository lock 순서를 따른다. - target과
repository.git의 path, ownership, permissions와 bare 상태를 다시 검증한다. origin의 canonical owner/repository identity가 요청과 일치하는지 확인한다. URL 문자열의 credential이나 transport hostname만으로 identity를 판정하지 않는다.- 명시적으로 검증한 origin에서 mirror refs를 prune/fetch한다. 임의 remote 교체, force reset 또는 다른 repository 재사용은 금지한다.
- Gitea에서 default branch와 SHA를 다시 읽고 local ref와 일치하는지 확인한다.
- 일치한 exact SHA를 이번 작업의 pinned source revision으로 기록한다.
- Issue workflow는 같은 handle에
release_repository를 request한다. 별도 non-Issue workflow는 자신의 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 경로를 기록하지 않는다. 최소 형식은 다음과 같다.
{
"schema": "gitea-agent.worktree/v1",
"owner": "<owner>",
"repository": "<repository>",
"issue": 1,
"base_sha": "<full-commit-sha>",
"issue_updated_at": "<authoritative-timestamp>",
"issue_body_sha256": "<sha256>",
"created_by_role": "gitea-impl-agent",
"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이며 신규 구현 producer는 정확히gitea-impl-agent를 기록한다. PID나 현재 process 존재 여부를 worktree 소유권 또는 활성 상태의 정본으로 사용하지 않는다.- Review admission은 schema가
gitea-agent.worktree/v1인 기존 state에 한해 정확히gitea-impl-agent또는 legacyimpl만 read-only compatibility로 허용한다. 이는 login/authentication alias가 아니며 그 자체로 permission을 부여하지 않는다. 누락, null, bool, list, dict, 빈 문자열, whitespace/case 변형과 다른 role은 거부한다. - 이 bounded exception은
state.json을 rewrite/migrate하지 않는다. admission 전후 exact bytes, mode, mtime과 inode를 보존한다. - 파일은 run-unique sibling temporary file에 mode
0600으로 완성하고 flush한 뒤 no-replace atomic rename으로 게시한다. 기존state.json을 덮어쓰지 않는다.
Worktree admission gate
Worktree를 신규 생성하거나 기존 worktree에서 구현을 재개하기 전에 다음을 모두 확인한다.
- exact
owner,repository와 Issue 번호가 검증됐고 brokerserve의 ready response로 Issue lease 보유를 확인했다. - Authenticated Gitea API adapter로 role identity, exact repository metadata, Issue body,
updated_at과 구현 승인 상태를 읽었다. - repository cache가 이 문서의 path, ownership, permission, bare, remote identity와 authoritative ref 검증을 통과했다.
- 구현에 사용할 exact base SHA와 그 출처를 결정했다. Discovery pinned revision과 현재 default branch SHA가 다르면 어느 한쪽을 자동 선택하지 않고 변경을 조사해 요구사항과 충돌하지 않는 base를 확정하거나 중단한다.
repository.git에 exact base SHA의 commit object가 존재한다. 문자열 형식이나 object 존재만으로 Gitea 또는 승인된 Discovery에서 유래했다는 검증을 대체하지 않는다.- canonical
checkout,state.json과 Git worktree registration의 존재 여부를 함께 조사하고 Issue container의 entry·type·owner·mode를 확인한다. 안전한runtime/·verification/만 존재하고 checkout·state·동일 Issue registration이 모두 없는 경우는 container-only 신규 생성 후보이며 기존 worktree가 아니다. Checkout·state·registration 중 일부만 있거나 unknown entry가 있으면 자동 cleanup/repair 없이 거부한다. - 신규 생성은 container 부재 또는 위 container-only 상태에서만 허용한다. 재사용은 일관된 checkout·state·registration이 존재하고 아래의 기존 worktree 검증을 모두 통과해야 한다. Runtime 생성 자체는 구현 승인이나 admission을 의미하지 않는다.
계획된 container-shape 확인은 같은 handle로 repository lease를 획득한 뒤 공통 helper로 실행한다. Helper는 exact Git registration을 read-only로 조회하며 다음 JSON container를 반환한다: absent, runtime-only, checkout-candidate, invalid. 앞의 두 상태는 신규 생성 후보일 뿐 구현 승인·base·cache 검증을 대신하지 않으며, checkout-candidate도 아래 기존 worktree 검증을 모두 통과해야 한다. invalid 또는 helper 오류는 자동 복구 없이 중단한다. 실제 state 계약은 state.json이고 별도의 registration.json을 만들지 않는다.
python3 <harness-root>/harness/lib/runtime_root.py inspect-worktree \
--root <harness-root> --owner <owner> --repository <repository> \
--issue <issue-number> --handle "$HANDLE"
Gate가 끝난 뒤 write 직전에 Issue body, updated_at과 구현 승인 상태를 다시 읽는다. admission snapshot과 다르면 worktree를 생성하거나 기존 worktree를 수정하지 않는다.
Worktree 신규 생성
- Broker Issue lease를 유지한 채 같은 handle에
acquire_repository를 request해 repository lease를 중첩한다. - cache와 base commit을 다시 검증한다. Broker가 준비한
worktrees와 Issue directory는 canonical mode·owner·type을 다시 검증하여 그대로 사용한다. Container 부재가 입증된 경로만 별도 안전한 생성 절차로 만들며 기존 경로를 덮어쓰거나 repair하지 않는다. - final
checkout,state.json과 동일 Issue의 Git worktree registration이 모두 없는지 다시 확인한다. git --git-dir=<repository.git> worktree add --detach <checkout> <full-base-sha>와 동등한 호출로 exact final checkout을 생성한다. branch 생성 option을 사용하지 않는다.- worktree를 Issue 용도로
git worktree lock하고 reason에 repository credential이나 민감정보 없이 Issue 번호를 기록한다. state.json을 state 계약에 따라 원자적으로 게시한다.- 생성 직후 다음을 모두 검증한다.
git rev-parse HEAD가 exact base SHA와 일치한다.git symbolic-ref -q HEAD가 성공하지 않아 detached HEAD임이 확인된다.git status --porcelain이 비어 있다.git rev-parse --git-common-dir가 canonicalrepository.git과 동일하다.git worktree list --porcelain에 exact checkout path가 한 번만 있고 locked 상태다.- checkout path와
state.json의 identity, ownership과 permission이 canonical 계약을 충족한다.
- 모든 검증 후 같은 handle에
release_repository를 request한다. Broker Issue lease는 해당 Issue workflow가 끝날 때까지 유지한다.
Git 명령의 성공 exit만으로 생성 완료를 판정하지 않는다. Git registration, checkout, state와 Issue snapshot을 모두 검증해야 한다.
기존 worktree 재사용
기존 checkout, state.json 또는 동일 Issue의 Git registration 중 하나라도 발견되면 신규 생성으로 덮어쓰지 않는다. 안전한 runtime/verification-only container는 이 재사용 조건에 포함하지 않는다. Broker Issue lease를 유지하고 같은 handle의 acquire_repository request로 얻은 repository lease 아래에서 다음을 모두 확인한다.
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 broker Issue lease 아래에서만 수행한다. 현재 worktree 외의 source, 다른 checkout 또는 installed package를 수정하거나 구현 근거로 사용하지 않는다.
- commit 전후 base SHA, HEAD SHA, staged·unstaged·untracked 상태를 기록한다. detached 상태를 local branch로 자동 전환하지 않는다.
- 명시적으로 승인된 push는 source와 destination을 모두 지정해
HEAD:refs/heads/<approved-branch>형태와 동등하게 수행한다. Canonical cache remote에remote.<name>.mirror=true가 설정돼 있으면 credential을 포함하지 않은 explicit repository URL을 사용하거나git -c remote.<name>.mirror=false push <name> HEAD:refs/heads/<approved-branch>와 동등하게 해당 호출에서만 mirror 동작을 끈다. Persistent remote config는 변경하지 않는다. 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할 수 있다. 이때 broker Issue lease를 유지하고 같은 handle의 acquire_repository request로 repository lease를 중첩한 뒤 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으로 우회하지 않는다.
제거할 때는 broker Issue lease를 유지하고 같은 handle의 acquire_repository request로 repository lease를 중첩한 상태에서 worktree lock을 해제하고 Git operation으로 checkout과 registration을 먼저 제거한다. 제거 전에 verification 계약에 따라 보존할 artifact identity, manifest·plan digest와 읽기 가능 여부를 확인한다. 두 대상의 부재를 확인한 뒤 exact state.json과 identity가 확인된 현재 run의 state temporary file만 제거한다. runtime/과 verification/은 서로 독립된 자원이며 worktree 제거 절차는 이를 삭제하거나 active broker/socket/handle을 건드리지 않는다. verification/이 남으면 Issue directory를 보존한다. runtime/만 남은 경우도 같은 보존 원칙을 적용하며, unknown entry·부분 생성 조합·identity/mode mismatch는 자동 복구·삭제하지 않고 partial로 보고한다. 마지막으로 exact checkout, Git registration과 state.json의 부재 및 verification 경로·digest가 제거 전과 같음을 재확인하고 release_repository를 request한다. 정상 보존된 runtime/verification 때문에 Issue directory가 남는 것은 checkout 제거 실패가 아니다. 비어 있는 Issue directory만 제거할 수 있으며 자동 만료·재귀 삭제를 도입하지 않는다.
Runtime 실행에서 만든 server·합성 DB·임시 credential은 해당 workflow가 종료 전에 정리·부재를 확인한다. Broker cleanup은 lease 해제·프로세스 종료·소유 socket 삭제·handle 기록 결과를 검증하며 기존 부분 실패 계약을 유지한다. Terminal handle과 run directory는 private 진단 자료로 남을 수 있으나 secret-bearing handle을 verification/reviewer 기록으로 복사하거나 token을 출력하지 않는다. 이 잔존 자체를 socket 정리 실패로 세지 않으며, stale run은 재사용·자동 삭제하지 않는다. 남은 runtime의 폐기는 exact 경로와 활성 자원 부재를 확인한 별도 승인 절차로만 수행한다.
Credential 안전
- credential 값은 role profile의
.env에만 저장하고 mode0600을 요구한다. - worker에게 credential 값 또는 credential-bearing URL을 전달하지 않는다.
- Git 인증이 필요하면 short-lived askpass 또는 동등한 child-process mechanism을 사용하고 종료 시 제거한다.
- inherited credential, 다른 role의 credential 또는 interactive prompt로 fallback하지 않는다.
- 인증에 실패하면 public clone이나 unauthenticated API·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을 더해 모두 다시 읽은 경우에만 완료로 판정한다.
Canonical external Issue verification artifacts
Shared workflow guard gates
Workflow guard는 기존 위임·test-first·계약 보존 규칙의 구조적 완료 gate다. 쓰기를 막는 sandbox, 기록된 주장의 진실성을 인증하는 수단, 자연어 의미 판정기 또는 자동 복구 도구가 아니다. 보호 경로 밖 변경이나 변경 후 원상 복구까지 없었다고 증명하지 않는다. 실제 승인 근거·관찰·assertion 검출력은 main agent와 독립 reviewer가 확인한다.
지원 CLI는 아래 네 가지다. ROOT는 사용이 이미 승인된 exact Git checkout root이며 raw 절대경로의 alias·symlink를 허용하지 않는다. INPUT과 JSON input은 이미 존재하는 bounded 기록이고 snapshot 출력의 parent는 실행 UID 소유 mode 0700이어야 한다. Helper는 directory를 만들거나 기존 권한을 repair하지 않고 새 snapshot만 mode 0600·no-clobber로 생성한다.
python3 <harness-root>/harness/lib/workflow_guard.py snapshot --repo-root ROOT --paths-json INPUT --output SNAPSHOT
python3 <harness-root>/harness/lib/workflow_guard.py compare-source --repo-root ROOT --before SNAPSHOT --paths-json INPUT
python3 <harness-root>/harness/lib/workflow_guard.py check-tests --input JSON
python3 <harness-root>/harness/lib/workflow_guard.py check-contracts --input JSON
- 기록 위치: 제품 checkout 밖의 기존
verification/<run-id>에 snapshot·inventory·command/result를 보존한다. 먼저verification_root.py prepare로 해당 run의 identity·lease·권한을 확인하고 기존evidence_manifest.py run/validate의 command/claim 기록에 연결한다. 경로·v2 manifest schema를 바꾸거나 generic run record를 추가하지 않는다. Spec의 native-review 운영 artifact는 기존 trustedharness/.runtime예외를 유지한다. Helper의 snapshot 출력은 검사 대상 Git root 밖이어야 하므로 harness 자체 유지보수에서 내부 진단 위치를 검사 대상 root의 예외로 자동 허용하지 않는다. - 읽기 전용 위임: 위임 전에 보호 경로 JSON array와 HEAD, 파일 존재·종류·identity·digest를 immutable protected scope로 고정한다. 결과 채택 전에 같은 경로 집합으로 비교한다. 시작 baseline 누락·source 변경·경로 집합 drift는 검토 근거를 무효화하며 PASS를 채택하거나 자동 restore하지 않는다. 쓰기 worker는 승인된 변경 allowlist 안에서 수정하고 main agent가 실제 diff·test를 확인한다. 허용된 편집을 읽기 전용 위반으로 취급하지 않는다. 최종 독립 검토 중에는 보호 대상 mutator를 병렬 실행하지 않는다. Spec reviewer의 immutable provider/Git blob binding과 moving checkout 사용 금지는 그대로이며 이 gate가 별도 checkout·제품 test 실행 권한을 주지 않는다.
- 기존 테스트: 첫 편집 전에 canonical collection의 ID·skip·expected-failure 상태와 collection command/source/output 근거를 기록한다. 새 regression 추가는 허용한다. 기존 ID 삭제·rename·새 skip/xfail에는 exact old-to-current mapping, 승인 근거와 대체/의미 검토 근거가 필요하다. 변경된 assertion·fixture·mock의 의미 검토도 별도로 연결한다. 같은 개수·이름·exit 0만으로 검출력 보존을 판정하지 않는다. 수집하지 않은 framework나 case를 수집 완료라고 기록하지 않는다.
- Finding 재현: 유효한 finding마다 current inventory의 named test와 구조화된 RED/GREEN 참조를 연결한다. 각 참조는 test ID, source revision/scope digest, command ID/argv, 실제·예상 exit와 evidence를 담는다. RED는 의도한 assertion/승인 동작의 nonzero 실패이고 GREEN은 exit 0이며 import·환경 오류를 RED로 인정하지 않는다. 반증된 지적에는 가짜 RED를 만들지 않는다. Helper는 참조 형식·연결·exit 조건을 확인하고 main agent는 실제 기록을 읽어 source binding과 실패 원인을 검증한다.
- 계약·단계: 변경 영향 범위의 기존 requirement뿐 아니라 API/CLI/응답 필드/상태 전이/cleanup 의미를 baseline inventory로 고정한다. 각 항목은 실제 current 항목과 검증 근거에 정확히 대응하거나 승인된 의도적 변경을 설명해야 한다.
missing·ambiguous는 승인 문자열로 통과시킬 수 없고, 실제 public-response 관찰이 미확인이면 observed로 바꾸지 않는다. 초기화 선행 조건의 모순·미지 stage·순환 precedence를 거부하되 정상 renew 등 lifecycle loop를 초기화 precedence로 모델링해 금지하지 않는다. - 판정: helper의
status=pass·exit 0은 제출된 구조적 gate가 통과했다는 뜻이며 제품 승인이나 evidence 인증을 대신하지 않는다.status=block/nonzero이면 해당 운영 근거를 채택하지 않고 누락·변경·판단 불가를 보고한다. 운영 gate 실패만으로 새로운 제품 acceptance criterion이나 자동REQUEST_CHANGES를 만들지 않는다.
Input schema와 엄격한 type/key 규칙은 harness/lib/workflow_guard.py의 API 계약을 따른다. paths-json은 중복 없는 기존 repository-relative protected 파일 array다. check-tests는 baseline_ids/current_ids, baseline_status/current_status, source·collection argv·inventory digest·evidence를 가진 baseline_collection/current_collection, authorized_changes, changed_tests, findings를 연결한다. check-contracts는 baseline/current/mappings와 stages/precedence를 연결한다. 파일별 의미를 임의 추론하지 말고 helper의 실제 schema와 기록을 검증한다. 이 문서의 정적 contract test 통과는 agent가 실행 중 절차를 준수했다는 증명이 아니다.
Test inventory Git source-binding preflight
Test inventory를 생성·전달하거나 채택하는 impl/spec/review main은 check-tests 이전에 harness/lib/test_inventory_binding.py로 collection의 Git source binding을 확인한다. 구조 guard는 그대로 유지한다. Expected revision과 stage는 inventory 자체에서 복사하지 않고 workflow가 독립적으로 고정한 baseline/current source와 collection 단계에서 가져온다. 사용이 이미 승인된 bare mirror 또는 .git directory만 사용하며 helper가 cache admission·lock·authenticated Git provenance를 대신하지 않는다.
Helper는 Linux의 신뢰된 /usr/bin/git와 /proc/self/fd/를 사용한다. PATH에서 Git을 탐색하지 않으며, admitted Git directory FD를 검증 전체와 모든 Git child에서 유지해 경로 치환이 읽기 대상을 바꾸지 않게 한다. 이 실행 환경이 없으면 fallback이나 repair 없이 차단한다.
python3 <harness-root>/harness/lib/test_inventory_binding.py \
--git-dir ABS_GIT_DIRECTORY --input BINDING_INPUT \
--expected-revision FULL_SOURCE_SHA --expected-stage COLLECTION_STAGE
python3 <harness-root>/harness/lib/workflow_guard.py check-tests --input GUARD_INPUT
Input 계약: 기존 inventory/manifest/collection 기록의 검증된 필드를 다음 bounded JSON input으로 연결한다. 이 adapter input은 제품의 새 문서·증빙 commit 의무나 기존 v2 evidence-manifest schema 변경이 아니다. 정본 verification run 또는 허용된 reviewer workflow artifact에 보존한다. Main은 binding input의 ids와 source_revision이 뒤의 check-tests baseline/current collection에 exact-match하는지도 확인한다. 입력 값의 자동 추정·재수집·권한 repair는 하지 않는다.
- Exact top-level keys:
schema,source_revision,stage,ids,file_sha256,collection. schema: exactgitea-agent.test-inventory-binding/v1.source_revision: lowercase 40자리 commit SHA.stage: 기존 collection 단계의 nonempty string. Baseline/current 명칭을 새로 강제하지 않고 independently expected stage와 대조한다.ids: 중복 없는repository-relative-path::symboltest ID array. Parameterized ID의 suffix는 보존한다. 실제 empty collection이면 빈 array를 허용하며 완전성은 main이 확인한다.file_sha256: 모든 ID에서 첫::앞의 파일 경로를 추출한 exact unique path set에 대응하는 lowercase SHA-256 map. 없는 파일·추가 파일·Git symlink/gitlink/tree를 일반 test blob으로 채택하지 않는다.collection: exact keyssource_revision,stage,argv,scope_paths,scope_sha256,evidence. Revision·stage는 top-level 및 independently expected source와 일치해야 한다.argv는 실제 collection 기록의 nonempty string array,evidence는 nonempty 실행 근거 참조다.scope_paths: 안전한 unique repository-relative path array이며 모든 test ID 파일을 포함한다. Collector 설정·fixture·dependency 등 실제 수집에 영향을 주는 입력도 main이 기록의 출처와 함께 검토한다.scope_sha256: exact commit의 각 scope regular blob에 대해SHA256(raw_bytes)를 계산한{path: digest}map을json.dumps(map, sort_keys=True, separators=(',', ':'), ensure_ascii=True)의 UTF-8 bytes로 직렬화한 SHA-256이다. Git object ID와 파일 SHA-256을 혼동하지 않는다.
판정 및 한계: CLI exit 0은 source binding PASS, 1은 revision/stage/file/hash/scope의 불일치 BLOCK, 2는 malformed input·안전하지 않은 경로 등 입력 거부다. Helper는 bounded JSON과 고정된 error/reason code만 반환하고 입력 본문·Git stderr·credential을 출력하지 않는다. Duplicate/unknown JSON keys, unsafe path, malformed SHA와 비정규 input은 거부한다. Git tree/blob만 읽고 checkout·fetch·test 실행·file/ref write를 하지 않는다. Dirty worktree 대신 pinned commit의 blob을 비교한다.
File/hash binding PASS는 named symbol·parameter 존재나 실제 collection 실행을 인증하지 않는다. argv/evidence의 형식과 source scope 결속을 확인해도 명령이 실행됐다는 증명은 아니다. Main이 실제 command/output/exit·환경·collection ID/status·범위의 적절성과 manifest 결속을 기존 provenance 규칙으로 별도 확인한다. 필요한 근거가 부족하면 해당 증빙을 채택하지 않으며 현재 collection을 baseline으로 재명명하지 않는다. 기존 revision evidence 재사용은 원래 source에 먼저 결속한 뒤 source-to-head 영향 평가를 유지한다. 별도 checkout이나 전체 테스트 재실행을 요구하지 않는다. Source-binding 실패를 제품 결함이나 자동 REQUEST_CHANGES로 전환하지 않는다.
Delegation adoption runtime consumption
harness/lib/delegation_adoption.py의 require_adoption(declaration, *, expected_source, expected_stage)는 실제 소비 함수의 진입 gate다. 독립 CLI의 이전 PASS receipt를 제출하는 대신 매 소비 호출에서 안전하게 context/result 파일과 bundle/Git bytes를 다시 읽고 verify_evidence를 실행한다. 선언 누락, 검증 실패, BLOCK, 입력 거부, 빈 delegated obligations, source/stage 불일치는 소비 동작 이전에 차단한다. main이 제공하는 expected_source와 expected_stage를 worker/context에서 역으로 만들지 않는다.
- 위임 근거를 쓰지 않는 경로도
{"mode":"none"}를 명시한다. 이는 main의 비사용 선언이지 자동 인증이 아니다. 위임 근거를 사용하면서 none으로 선언하거나 parse-only 내부 primitive로 판정을 우회하는 것은 금지한다. 기존 계약/source/identity/lease 검증은 생략되지 않는다. - 위임 근거를 사용하는 선언의 exact fields는
mode,bundle_root,context_path,result_path,git_dir이며 mode는delegated다. 경로는 canonical absolute safe paths이고git_dir는 경로 또는 null이다. Adoption의 lexical 검사 이후 동일 공통 검증기의_json_file/_directory_fd/_open_rel이 모든 ancestor와 마지막 component를 descriptor-relativeO_NOFOLLOW로 읽어 symlink를 거부한다. Git 조회는 한 invocation에서 retained FD를 사용한다. 사전 realpath 비교가 실제 안전 읽기를 대신하지 않으며 이 계약은 임의 동시 mutator 아래 byte-stability나 atomic consume을 보장하지 않는다. PASS receipt나 status 필드를 입력으로 받지 않는다. - Spec 판정 artifact의
render_review는 target.head로 expected Git source를 고정하며 stage는spec-review다. Parse/readback의 내부 canonical re-render는 판정 채택이 아니므로 명시적 none을 사용한다. - Impl 재작업 delivery artifact의
rework_comment.py render/do_render는 final_head에 고정한 Git source와implementation-deliverystage를 사용한다. 최초 implementation main의 production 진입점은harness/agents/impl/skills/gitea-issue-implementation/scripts/implementation_delivery.py의deliver_implementation(adapter, *, head, declaration)다. 이 workflow는 canonicaldelivery_adoption.deliver를 실제 호출해 fresh gate → authenticated adapter push → exact remote-head readback → fresh gate → create/reuse PR → exact PR number/head/open readback을 순차 수행한다. Gate failure 또는 remote drift이면 이후 write를 중단하고, 외부 write 실패를 자동 재시도하지 않는다. Adapter의 실제 반환값/예외를 그대로 전달하며 callback 호출만으로 delivered/verified 상태를 만들어 내지 않는다. 실제 transport의 push/PR create 및 remote readback은 main-owned adapter의 책임이며, gate 밖 arbitrary Git/API 호출을 sandbox로 막는다는 의미는 아니다. 재작업 comment renderer만으로 최초 delivery 전체가 보호된다고 주장하지 않는다. - Review
native_review.publish는 plan.target.head와code-reviewstage로 시작 시 확인하고, provider/lease readback 후create_review직전 다시 확인한다. 실패하면 POST를 하지 않으며 intent가 이미 기록된 경우 기존 at-most-once reconciliation 규칙을 유지한다. Evidence 변경을 이유로 자동 POST 재시도하지 않는다. - Spec/rework의 render 소비 CLI는
--no-delegated-evidence또는--delegation-adoption ABS_DECLARATION_JSON중 하나를 명시한다. none은 전자, delegated 선언은 후자다. 문서의 예전 render 명령에도 이 선택을 반드시 추가한다. Native publication은 Python publish API이며 CLI publish 명령을 새로 만들거나 존재한다고 주장하지 않는다. Python consumer는 declaration keyword를 명시한다. 증빙 검증 자체 CLI, parse/readback, 기존 source/tests guard는 별개의 read-only 도구다. - 소비 role adapters의 source는 exact
git_tree/revision이다. Captured worktree의 base_revision을 최종 head의 증명으로 바꾸지 않는다. Common API의 captured_worktree 지원은 독립 expected source가 동일한 다른 소비자의 사용을 위한 것이며 role final-head binding을 완화하지 않는다.
실행 회귀는 실제 소비 함수에 정상/차단/입력 거부/선언 누락/이전 PASS 이후 변경을 넣어 판정 artifact 생성·delivery output write·publication adapter 호출이 차단되는지 검사한다. 이것은 local synthetic integration이며 live Gitea 게시·push 증거가 아니다. Runtime gate 역시 sandbox 또는 capability enforcement가 아니고, caller의 위임 비사용 선언·context 독립성·자연어 판정 의미·OS 권한을 인증하지 않는다. 최종 검증과 소비 사이에 mutator를 병렬 실행하지 않는 기존 규칙은 유지한다.
Delegation evidence acceptance preflight
위임 결과를 판정·delivery·publication 증빙으로 채택하기 이전에 main이 harness/lib/delegation_evidence.py를 실행한다. 기존 source-preservation snapshot/compare-source, test inventory source-binding, check-tests는 각각 유지한다. 이 gate는 보호 범위 변경 검출이나 collection completeness 검사를 대신하지 않는다.
python3 <harness-root>/harness/lib/delegation_evidence.py \
--bundle-root ABS_FROZEN_BUNDLE --context ABS_MAIN_CONTEXT_JSON \
--result ABS_WORKER_RESULT_JSON --git-dir ABS_ADMITTED_GIT_DIRECTORY
--git-dir는 git_tree source에서 필수이며 captured_worktree에서는 Git provenance를 주장하지 않으므로 생략할 수 있다. Linux의 신뢰된 /usr/bin/git, pinned directory FD와 /proc/self/fd/를 사용한다. Helper는 기존 자료만 읽으며 file/ref write, source import, eval, checkout/fetch, test 실행, credential 조회, 환경 repair를 하지 않는다. Context/result input, frozen bundle source와 evidence는 canonical absolute root 아래의 bounded regular files여야 한다. Alias·symlink ancestor·FIFO·duplicate/unknown JSON keys·잘못된 type을 거부한다.
독립 trust 입력: main이 source identity, 실제 bundle bytes, stage, 허용 작업, 승인된 검증 항목, 실제 command/output/exit와 실패 원인을 검토하여 context와 독립 실행 catalog를 고정한다. Worker의 주장·테스트 개수·자체 failure_kind를 그대로 복사하여 trust 입력을 만들지 않는다. 위임 시작 시점 bundle과 채택 대상 source가 다르면 새 source의 입력·기록을 main이 독립적으로 다시 결속한다. 이전 snapshot의 PASS를 다른 source에 재사용하지 않는다. 이 gate는 context의 신뢰성을 스스로 인증하지 않는다. Context/catalog의 인증·독립성, evidence의 실제 실행 provenance와 assertion 검출력은 main 책임이다. Secret-bearing source/output를 무작정 bundle로 복사하지 않으며 record와 digest가 필요해도 credential 값은 기록하지 않는다.
Main context 계약: exact top-level keys는 schema, source, stage, bundle_sha256, files, allowed_actions, obligations, executions이다.
schema: exactgitea-agent.delegation-evidence-context/v1.source: exact variant{kind: "git_tree", revision: lowercase40}또는{kind: "captured_worktree", base_revision: lowercase40}.git_tree는 exact commit과 regular Git blob까지 확인한다.captured_worktree는 main이 별도로 고정한 pending-change snapshot의 bytes를 검사하며 base revision과 동일한 내용 또는 commit된 source라고 주장하지 않는다. 두 mode를 서로 재명명하지 않는다.stage: 독립적으로 고정한 nonempty 단계 문자열.files: 추출한 source의 안전한 repository-relative path → lowercase SHA-256 exact map. 실제 frozen file bytes와 대조한다. 추출 범위는 listed files only이고 bundle에 없다는 이유로 repository 부재로 판정하지 않는다.git_tree에서는 snapshot bytes와 같은 pinned commit blob 모두에 결속한다.bundle_sha256:SHA256(json.dumps(files, sort_keys=True, separators=(',', ':'), ensure_ascii=True).encode('utf-8')). Mutable 실행 metadata는 source map에 억지로 포함하지 않으며 별도 catalog/evidence digest로 결속한다.allowed_actions: 중복 없는static/executesubset. Context에서 지정한 worker의 허용 작업이며 자동 실행 권한을 부여하거나 숨겨진 실행을 탐지하는 sandbox가 아니다.obligations: unique{id: nonempty, kind: "static" | "execution" | "red_green"}array. Main이 승인된 범위에 필요한 항목만 지정한다. 모든 제품 입력의 Cartesian product나 새 제품 요구사항을 만들지 않는다.executions: main이 독립적으로 검토한 실행 catalog array. 각 record의 exact keys는id,origin,test_id,bundle_sha256,test_file_sha256,argv,exit_code,failure_kind,evidence_path,evidence_sha256이다.id는 unique nonempty string,test_id는 named regression의path::symbolID이며 일반 실행-only record에서는 빈 문자열을 허용한다. 빈 ID/placeholder test digest는 named-test 검증 근거가 아니고red_green에는 사용할 수 없다. Digest는 lowercase64,argv는 nonempty string array, exit는 bool이 아닌 integer다.failure_kind는none/assertion/approved_behavior/import/environment/timeout이다. Relativeevidence_path는 bundle root 아래 별도 immutable 실행 기록을 참조하며 실제 regular file bytes가evidence_sha256와 같아야 한다. Source map에 실행 metadata를 넣어 source를 순환 결속하지 않는다.- Catalog
origin은direct/inherited이며 main이 실제 actor·관찰 출처를 대조한다.direct실행 인용은execute허용이 있어야 한다.inherited는 이미 검토된 다른 producer의 기록을 static-only worker가 인용하는 경우이며 직접 실행했다고 인정하지 않는다. Worker는 catalog origin을 재정의할 수 없다. Hash와 metadata 형식의 PASS 자체는 실행 인증이 아니다.
Worker result 계약: exact top-level keys는 schema, source, stage, bundle_sha256, claims이다. schema는 exact gitea-agent.delegation-evidence-result/v1이고 source/stage/bundle digest는 main context와 exact-match해야 한다. claims의 exact record는 {id, status, anchors, execution_ids}이다. Claim ID set은 main obligations와 exact-match해야 하고 누락·추가·중복은 거부한다. status는 supported/indeterminate; indeterminate는 PASS로 채택하지 않는다. execution_ids는 중복 없는 기존 독립 catalog ID 참조이며 worker가 새 실행 record를 스스로 만들어 넣는 형식이 아니다.
Anchor variant의 exact keys와 검증은 다음과 같다.
{kind: "lines", path, start_line, end_line, text_sha256}: 파일 범위 내 positive integer line range와 rawsplitlines(keepends=True)byte slice의 SHA-256을 대조한다. 인용 text의 존재를 검증할 뿐 주장한 동작과 text가 의미적으로 일치한다는 판정은 main이 한다.{kind: "test", path, symbol}: Python 파일을 실행/import하지 않고 AST에서 qualified named test function/method의 존재를 확인한다. Local nested function·dynamic 생성·파싱할 수 없는 source를 실제 수집된 test로 추정하지 않는다. named symbol 존재는 assertion 검출력을 인증하지 않는다. Pytest parameter instance/collection completeness도 인증하지 않는다.{kind: "absence", path}:git_tree의 authoritative exact Git tree에서만 부재를 확인한다. Git 조회 실패나 bundle 누락을 repository 부재로 판정하지 않는다.captured_worktree의 부재 주장은 authoritative current-tree 근거가 없어 indeterminate/BLOCK으로 남긴다.
Static obligation은 유효 anchor를 요구하고 execution obligation은 독립 catalog 참조를 요구한다. red_green은 실제 snapshot에 저장된 named test와 같은 ID/file SHA-256의 RED·GREEN을 연결한다. RED는 nonzero assertion/approved_behavior, GREEN은 exit 0이며 GREEN source bundle digest가 채택 대상과 일치해야 한다. TDD의 RED bundle digest는 수정 전 source일 수 있으므로 GREEN으로 재명명하지 않고 독립 기록의 원래 scope를 유지한다. import·환경 오류는 RED로 인정하지 않는다. Missing regression/record, 잘못된 test/hash/scope, unauthorized direct execution과 출처 불명 기록은 채택을 차단한다.
판정: CLI exit 0은 증빙 연결 PASS, 1은 불일치·indeterminate BLOCK, 2는 입력 거부다. 고정된 reason codes/counts의 bounded JSON만 출력하며 source·argv·입력값·Git stderr·credential을 echo하지 않는다. BLOCK/입력 거부를 자동 제품 결함이나 REQUEST_CHANGES로 바꾸지 않는다. 해당 주장을 판정 근거에서 제외하고 main이 원본을 직접 재검증하거나 미검증으로 보고한다. Gate의 PASS는 의미 검토, 실제 실행 관찰, 외부 게시 readback 또는 전체 작업 완료의 증명이 아니다.
이 JSON은 기존 자료를 잇는 harness adapter 입력이다. 정본 verification run 또는 허용된 reviewer workflow artifact에 보존하며 제품의 추가 evidence commit이나 전체 테스트 재실행 의무를 만들지 않는다. 별도 collector 실행, 제품별 테스트 정책, manifest v2 변경 또는 role의 제품 실행 제한 완화를 추가하지 않는다. Skill/document wiring 테스트는 소비 workflow가 실제 이 절차를 실행했다는 증명이 아니다.
Nested input contracts
All object keys below are required and exact; unknown/missing keys, duplicate JSON keys, duplicate list identities, incorrect scalar types and non-regular/unbounded JSON inputs block. Empty strings are allowed only where explicitly stated. References are nonempty strings identifying existing verification command/result or approval records, not their secret contents. Full source revision is lowercase 40-hex; byte/scope digests are lowercase 64-hex. Integer fields reject booleans. CLI exit codes are 0 for structural PASS, 1 for detected violations/drift, 2 for rejected input; APIs raise GuardError for rejected input or return a status/violations result.
Test input: exactly baseline_ids, current_ids, baseline_status, current_status, baseline_collection, current_collection, authorized_changes, changed_tests, findings.
- ID arrays contain unique nonempty strings. Each status object has exactly that inventory's ID keys; each value is
{"skip": boolean, "expected_failure": boolean}. No omitted status is silently treated as false. - Each collection object is
{source_revision: full SHA, command_id: reference, argv: nonempty array of nonempty strings, inventory_sha256: digest, evidence: reference}. Digest is SHA256 over UTF-8 bytes ofjson.dumps({"ids": sorted(ids), "statuses": {id: statuses[id] for id in sorted(ids)}}, sort_keys=True, separators=(',', ':')); use Python's default JSON escaping. Main verifies collection actually covered the declared frameworks/cases. authorized_changesis an array of{old_id: baseline ID, kind: "delete" | "rename" | "status", replacement_ids: unique current ID array, reason: nonempty string, approval: reference, evidence: semantic-review reference}. A delete requires the old ID absent and an empty replacement; a rename requires the old ID absent and at least one actual current replacement; status requires the old ID still present and exactly[old_id]. Each old ID has at most one approval row. No replacement is silently invented or reused by another approval. Unclassified empty mappings block. Ordinary added regression IDs do not require removal approval. Every removed ID and newly introduced skip/xfail is covered by a compatible explicit approval; new skipped tests need explicit replacement coverage, not an unrelated deletion.changed_testsis an array of{test_id: current ID, baseline_sha256: digest, current_sha256: digest, review_evidence: reference}, unique by test ID. Main inventories actual assertion/fixture/mock edits; helper cannot discover an omitted changed-test declaration or prove semantic preservation.findingsis an array of{id: unique nonempty string, test_id: current ID, classification: "assertion" | "approved_behavior", red: execution, green: execution}. Each execution is{test_id: same finding test, source_revision: full SHA, scope_sha256: digest, command_id: reference, argv: nonempty string array, exit_code: integer, expected_exit_code: same integer, failure_kind: string, evidence: reference}. RED requires positive nonzero exit and failure_kindassertionorapproved_behavior; GREEN requires exit 0 and failure_kindpass. Import/environment failure is invalid RED. Main inspects actual logs and ties each command to the named test and its exact tested source; expected/actual code equality alone does not prove that.
Contract input: exactly baseline, current, mappings, stages, precedence.
baselineis a nonempty array of{semantic_key: unique nonempty string, baseline_revision: full SHA, path: repository-relative path without empty/dot/traversal segments, meaning: nonempty string}. It inventories old semantics, not merely current document headings.currentis an array of{semantic_key: unique nonempty string, meaning: nonempty string, observation: "observed" | "not_applicable" | "unknown", evidence: reference}.unknownblocks even with an evidence string; justified not_applicable is not a fabricated public-response observation. Main must check actual source/static contract and public response as applicable.mappingsis one row per baseline semantic key:{semantic_key: baseline key, current_keys: unique current-key array, status: "preserved" | "intentionally_changed" | "missing" | "ambiguous", authorization: string, verification: string}. Resolved rows require nonempty current_keys and verification; intentionally_changed additionally requires explicit authorization. Authorization may be empty for preserved rows. Missing/ambiguous always block even with authorization. New current keys and approved merges are permitted with complete old-to-current coverage: every old key retains its own row and semantic verification, and every current key is referenced. Several old rows may reference one merged current row; this is not permission to omit old meanings. Main checks preservation/substitution/merge semantics and real approvals independently.stagesis a nonempty unique string array;precedenceis an array of unique[prerequisite, dependent]string pairs, both declared stages. Initialization precedence must be acyclic. Do not encode allowed renew/lifecycle loops as startup prerequisites.
Binding procedure (shape-only, not evidence authentication): collection inventory digest binds submitted ID/status data, not its truth. Helper does not open/authenticate evidence/approval references or compare their source_revision/scope_sha256 to a checkout. Before adopting PASS, main reads the referenced existing immutable command/result and approval records, verifies exact HEAD/provider blob and relevant source digests, actual argv/environment/exit/output and named RED failure cause, then independently reviews changed test/contract semantics. Missing/unreadable/wrong-source evidence is evidence_gap, not a product defect by itself. Product execution remains in existing verification/<run-id> with v2 manifest run/validate and exact-head readback; spec uses immutable authoritative blobs and its existing trusted runtime exception, never a moving implementation checkout or newly mandated product execution.
The following two inputs are runnable synthetic structure examples only; the a revision and synthetic evidence strings are not genuine verification records and cannot be adopted as product evidence. They demonstrate the exact nested shape and actual calculated inventory digest. For production, replace them with verified collection, source and evidence values before gate adoption.
{
"baseline_ids": [
"case_a"
],
"current_ids": [
"case_a"
],
"baseline_status": {
"case_a": {
"skip": false,
"expected_failure": false
}
},
"current_status": {
"case_a": {
"skip": false,
"expected_failure": false
}
},
"baseline_collection": {
"source_revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"command_id": "fixture-collection",
"argv": [
"python3",
"-B",
"-m",
"unittest",
"case_a"
],
"inventory_sha256": "b2fac8d5f9e294a3d2c642955deab1bbf3bf8c9cf696526be56f2661b7eba455",
"evidence": "synthetic-collection-record"
},
"current_collection": {
"source_revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"command_id": "fixture-collection",
"argv": [
"python3",
"-B",
"-m",
"unittest",
"case_a"
],
"inventory_sha256": "b2fac8d5f9e294a3d2c642955deab1bbf3bf8c9cf696526be56f2661b7eba455",
"evidence": "synthetic-collection-record"
},
"authorized_changes": [],
"changed_tests": [],
"findings": []
}
{
"baseline": [
{
"semantic_key": "ready-fields",
"baseline_revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"path": "src/broker.py",
"meaning": "Public ready fields are handle/socket."
}
],
"current": [
{
"semantic_key": "ready-fields",
"meaning": "Public ready fields are handle/socket.",
"observation": "observed",
"evidence": "synthetic-response-record"
}
],
"mappings": [
{
"semantic_key": "ready-fields",
"current_keys": [
"ready-fields"
],
"status": "preserved",
"authorization": "",
"verification": "synthetic-contract-record"
}
],
"stages": [
"runtime",
"ready",
"admission"
],
"precedence": [
[
"runtime",
"ready"
],
[
"ready",
"admission"
]
]
}
Invalid examples: rename with replacement_ids: [] blocks (not an implicit deletion); expected_exit_code: true blocks; unknown observation or missing contract with nonempty authorization blocks; duplicate current contract rows and precedence cycles block. Static documentation tests establish documentation/schema consistency only, not agent runtime compliance.
Issue별 검증 실행 기록의 정본은 <harness-root>/repositories/<owner>/<repo>/worktrees/issue-<number>/verification/<run-id>이다. <harness-root>는 실제로 읽은 harness/operations/repository-cache.md에서 결정한다. 제품 source는 형제 checkout/에 있고 verification은 제품 Git tree 밖에 있다. 구현·재작업의 회차별 plan, evidence manifest, command output, RED/GREEN 결과, finding matrix와 head별 provenance를 제품 docs/verification/에 새로 누적하거나 evidence-only commit을 만들지 않는다. 승인된 제품 요구사항이 명시적으로 요구하는 정본 문서와 지속적인 테스트·synthetic fixture·재현 스크립트는 제품에 유지할 수 있다. 기존 파일을 자동 이동·삭제하거나 광범위 .gitignore 변경으로 처리하지 않는다.
- 경로 identity는 인증된 exact owner, repository와 양의 Issue 번호에서 결정한다. 기존 cache identifier 규칙을 따르며
issue-<number>는 선행 0 없는 십진수다.<run-id>는[A-Za-z0-9][A-Za-z0-9_.-]{0,127}형식의 새 식별자다. Stage, source revision 또는 실행을 바꾸면 새 run-id를 사용한다. Dot segment, 절대 artifact file 경로, traversal, symlink와 임의 temporary/artifact root는 거부한다.<owner>/<repo>를 성공한 lookup 결과에 맞춰 자동 정규화하지 않는다. - evidence
run전에 다음 명령으로 live broker lease와 exact harness root·Issue identity를 인증한다:python3 harness/lib/verification_root.py prepare --root <harness-root> --owner <owner> --repository <repository> --issue <issue> --run-id <run-id> --handle <handle>. 이 helper는verification/과<run-id>/를 각각 별도 생성하고, 각 directory가 실행 UID 소유 mode0700인지 생성 직후 다시lstat한다. Symlink, owner·mode 불일치와 path collision은 거부하며, 기존 malformed root를 자동 chmod하거나 repair하지 않는다.Path.mkdir(parents=True, mode=0o700)는 중간 parent가 umask를 따르므로 이 경로에 사용하지 않는다. - Caller가 인증된 Issue identity와 cache/worktree admission을 확인하고 broker Issue lease 아래 위 prepare를 실행한다. Directory는 실행 UID 소유 mode
0700, plan·manifest·output 파일은 실행 UID 소유 mode0600으로 만들고 즉시lstat으로 확인한다. 기존 cache bootstrap, repository lock과 lease 계약은 바꾸지 않는다. Artifact helper는 lease·Gitea identity·Git registration을 대신 인증하거나 directory를 만들고 cache/권한을 복구하지 않는다. repo_root는 canonicalcheckout,artifact_root는 exactverification/<run-id>로 분리한다. Plan의hash_paths는 repository-relative source 경로이고 plan/output/manifest 인자는 run-relative artifact 경로다. 두 namespace는 이름이 같아도 서로 다른 파일이다. Source tree의 tracked·untracked·ignored 파일을 함께 검사하고 evidence 경로 exclusion을 허용하지 않는다. Python은-B또는 allowlist에 선언한PYTHONDONTWRITEBYTECODE=1, pytest는 필요하면-p no:cacheprovider로 checkout 안의 자동 cache 생성을 막는다. 테스트 임시 산출물·dependency environment도 checkout 밖 승인된 경로를 사용한다. Helper가 ignored cache를 자동 삭제하거나 clean 상태로 간주하지 않는다.- 새 실행은
evidence-manifest/v3로 source full SHA, tracked source blob SHA·byte digest, owner/repository/Issue/run-id, plan path·digest, 실제 argv·exit code·환경·bounded output·시간과 claim pointer를 결합한다. Existing manifest는 no-clobber 원자 게시로 덮어쓰지 않는다. 완료 기록은 불변으로 취급하고 실패·재실행도 새 run-id에 보존한다. Helper의validate성공은 local artifact 정합성일 뿐 원격 PR head 또는 provider readback 검증을 대체하지 않는다. - Baseline, integration, precommit과 remote-head 검증은 각 stage에서 검증한 그 commit의 clean source에 결합한다. Dirty 개발 tree는 보존하되 helper가 성공 증거로 게시하지 않는다. Final tracked 제품 산출물을 commit한 뒤 전달할 exact commit에서 검증하고, 실패하면 push하지 않는다. 모든 PR/결과 보고는 같은 immutable record의 command/result를 사용한다.
- Same-UID 배포에서는 인증된 reviewer가 producer 기록을 read-only로 읽는다. 다른 UID나 host에서는 승인된 credential-free read-only export/immutable artifact 전달 수단을 명시한다. 전달 receipt는 repository/Issue/run-id, source SHA, manifest·plan·output digest와 실제 읽기 검증을 묶는다. UID가 다르다고 chmod/chown하거나 임의 sibling path를 허용하지 않는다. 경로 문자열이나 구현자 요약만으로 reviewer-accessible이라고 판단하지 않으며, 접근/무결성/source binding 실패는
evidence_gap이다. - Reviewer는 검증 source를 immutable provider/Git blob에 독립 결합한다. Producer manifest의 테스트 결과는 구현자 증거로 표시하고 reviewer 자체 실행으로 표현하지 않는다. Spec native-review snapshot/result와 운영 자료는 trusted
harness/.runtime에 남겨도 되며 제품 실행 evidence와 혼동하지 않는다. Spec reviewer는 moving implementation checkout을 근거나 test 실행 장소로 재사용하지 않는다. - Checkout 제거, broker release와 verification 폐기는 별개다. Checkout과 state를 제거해도 verification과 그 Issue directory는 보존한다. 보존 기간의 자동 만료나 재귀 삭제를 도입하지 않는다. Artifact 폐기는 별도의 exact 경로 승인, 활성 consumer·delivery/readback 의존성 조사와 digest 기록을 거쳐야 한다. 생성 실패의 미완성 run도 경로 identity가 입증된 현재 run 범위만 정리할 수 있고, 완료 기록은 자동 삭제하지 않는다.
- 인증정보를 사용했다는 사실은 남길 수 있지만 값·토큰·비밀번호·cookie·연결 문자열은 보존하지 않고
[REDACTED]로 기록한다. 새 증거를 만들기 위해 credential을 plan/argv/output에 넣지 않는다. Helper는 알려진 secret·header 유출을 fail-closed하고, 이것을 완전한 sandbox 또는 모든 비밀에 대한 자동 탐지로 과장하지 않는다. - 기존
evidence-manifest/v1은 명시적validate-legacy --repo-root <historical-checkout> --manifest <repository-relative-manifest>로 read-only 호환 확인만 한다. 새run/validate에는 v1·repository-relative evidence 생성 fallback이 없다. Archived replay는 fresh source 실행·원격 head/readback 또는 새 publication 완료를 증명하지 않는다. Migration도 별도 승인 없이 기존 제품 evidence를 수정하지 않는다.
Command deadline 계약: 새 plan의 각 command는 timeout_seconds를 명시한다. Boolean·문자열·누락·비유한 값·0 이하·3600 초 초과를 실행 전에 거절하며 암묵적인 무제한 실행을 허용하지 않는다. Monotonic wall-clock deadline은 무출력 대기, stdout EOF 이후 실행, leader 종료 뒤 descendant가 유지한 pipe까지 적용한다. 각 command는 별도 session/process group에서 실행하고 timeout·출력 초과·예외 시 해당 process group을 TERM → 제한된 grace → KILL로 정리하고 direct child를 bounded wait로 회수한다. Leader가 종료됐어도 같은 group의 살아 있는 descendant를 남기지 않는다. 이것은 sandbox가 아니며 별도 session으로 이탈한 descendant의 containment나 grandchild 직접 회수를 보장하지 않는다.
evidence-manifest/v3는 실제 timeout, execution_status, termination의 term_sent·kill_sent·direct_child_reaped·group_cleanup_completed boolean 기록과 안전한 bounded 부분 출력을 결합한다. 새 claim pointer는 기존 6개에 timeout·실행 상태·종료 기록을 추가한 9개다. Linux /proc로 같은 group의 non-zombie 프로세스를 확인하며 확인·정리에 실패하면 cleanup_failed로 차단한다. TERM grace는 0.25 초, KILL 후 group 종료·direct-child 회수 예산은 0.5 초이며 오류 fallback wait도 0.1 초로 제한한다. 새 command의 max_output_bytes는 0–1048576 범위의 non-boolean 정수만 허용한다. execution_status는 completed|timeout|output_limit|cleanup_failed|descendant_cleanup이다. 정상 종료 leader 뒤의 살아 있는 descendant를 신호로 정리한 경우도 descendant_cleanup 실패이며, completed에는 TERM/KILL 신호가 있을 수 없다. Timeout·출력 한도 초과·정리 실패는 expected exit code와 같아도 성공이 아니며 TDD RED 또는 제품 assertion 실패로 분류하지 않는다. 해당 실행은 실패로 보존하고 뒤의 command를 실행하지 않으며, 실행된 prefix의 claim만 기록한다. Partial output에도 기존 secret 탐지를 적용하고 인증정보 유출은 fail-closed한다. 자동 재실행은 하지 않으며 새로 승인된 실행에는 새 run-id를 사용한다.
기존 evidence-manifest/v2는 명시적 validate-archived-v2 --contract-path <canonical-contract> --repo-root <checkout> --artifact-root <canonical-run> --manifest <run-relative-manifest>로 read-only 호환 검증한다. Canonical identity, exact current HEAD, source/plan/output binding을 유지하지만 과거 기록에 없던 timeout이나 종료 보장을 추론하지 않는다. 새 run/validate에는 v2 fallback이 없다. v1은 기존 validate-legacy만 사용한다. 두 archive 경로의 성공은 새 실행·TDD RED·fresh publication proof가 아니며 기존 artifact를 수정하거나 새 schema로 자동 변환하지 않는다.
Canonical native-review journal
Native publication의 retained root는 <harness-root>/harness/.runtime/native-review-journals이며 repository cache/worktree 및 caller의 run directory와 별개의 binding registry다. canonical_journal_path(plan) 계산은 권한 증명이 아니다. Authenticated origin/repository/Issue/PR/head/contract/reviewer binding, full-plan 충돌, durable presence anchors, mode0700/0600 및 nonblocking mutex 정책을 적용한다. Alternate path와 missing/interrupted 상태를 자동 repair하거나 새 POST의 근거로 사용하지 않는다.
기존 intent의 재개는 read-only reconciliation뿐이며 legacy는 사전 digest·현재 role/lease/adoption 검증 후 명시적 register_legacy_journal로만 등록한다. Import pending receipt를 복사 전에 fsync하고 완료 receipt 전 게시를 차단한다. 원본 journal을 자동 이동·삭제하지 않는다. 외부 write와 canonical Issue lease의 acquire/renew/final verification/release/cleanup 계약은 그대로 유지한다. Same-install cooperative filesystem만 보호하며 cross-host global exactly-once를 주장하지 않는다.
구체적인 registry/legacy/retention 계약은 native review reference를 정본으로 따른다. .gitignore의 /harness/.runtime/ 보호를 유지하며 journal을 Git commit·source evidence bundle에 포함하지 않는다.