Compare commits

...
2 Commits
3 changed files with 31 additions and 2 deletions
@@ -40,10 +40,11 @@ Bootstrap 이후 code review는 canonical [Repository cache 운영 지침](../..
## 진입 조건 ## 진입 조건
1. 식별자와 review role의 인증 출처·stable account ID를 검증한다. Impl 및 spec role과 다른 identity여야 하며 다른 role credential로 fallback하지 않는다. 1. 식별자와 review role의 인증 출처·stable account ID를 검증한다. Impl 및 spec role과 다른 identity여야 하며 다른 role credential로 fallback하지 않는다. 현재 요청의 원문·대상·승인 범위와 확인 가능한 식별자(예: session/message ID)를 독립적으로 대조해 검증된 요청 참조를 기존 workflow artifact에 고정한다. workflow label이나 run-id suffix에서 요청 식별자를 추론하지 않는다. 이 label들은 게시 권한 근거가 아니다.
2. PR이 open이고 source Issue에 exact `Closes #<issue-number>` 하나로 연결되며 default branch를 base로 하는지 확인한다. 2. PR이 open이고 source Issue에 exact `Closes #<issue-number>` 하나로 연결되며 default branch를 base로 하는지 확인한다.
3. 현재 계약 digest와 PR full head SHA에 결합된 spec-agent의 `approve/APPROVED` native review를 확인한다. Author, body binding, stale·dismissed 상태를 검증한다. 이전 head의 pass나 구현자의 pass 주장은 인정하지 않는다. 3. 현재 계약 digest와 PR full head SHA에 결합된 spec-agent의 `approve/APPROVED` native review를 확인한다. Author, body binding, stale·dismissed 상태를 검증한다. 이전 head의 pass나 구현자의 pass 주장은 인정하지 않는다.
4. Issue 계약, PR base·head, 전체 changed files·diff·reviews·review comments와 CI/status를 snapshot으로 보존한다. Pagination 누락, truncated diff, 계약 모순 또는 identity 불명은 차단한다. 4. Issue 계약, PR base·head, 전체 changed files·diff·reviews·review comments와 CI/status를 snapshot으로 보존한다. Pagination 누락, truncated diff, 계약 모순 또는 identity 불명은 차단한다.
5. 고비용 리뷰·테스트 실행 전에 canonical journal 상위 경로를 읽기 전용으로 사전 점검한다. 검증된 설치의 `$HARNESS_ROOT/harness/.runtime`이 실제 publisher의 canonical journal parent와 같은지 확인하고, 기존 경로는 `lstat`으로 symlink가 아닌 디렉터리, 현재 review role 소유, 정확한 mode `0700`인지 검사한다. 불일치·확인 불가는 blocker로 보고하고 운영자 승인에 따른 exact-path 조치를 요청하거나 게시하지 않는 `local-only` 범위를 명시적으로 정한다. 자동 chmod/chown, journal 이동, guard 완화는 금지한다. 경로 부재는 통과로 기록하지 않고 기존 생성 규칙에 따른 준비·확인 여부를 구분하며 새 자동 생성·복구 절차를 추가하지 않는다. 이 사전 점검은 게시 승인이나 기존 publish-time identity·lease·source·journal 검사를 대신하지 않는다.
완료 조건: 현재 head와 계약에 대한 유효한 spec pass 및 완전한 리뷰 입력이 있다. 실패하면 read-only blocker를 보고하고 native review를 게시하지 않는다. 완료 조건: 현재 head와 계약에 대한 유효한 spec pass 및 완전한 리뷰 입력이 있다. 실패하면 read-only blocker를 보고하고 native review를 게시하지 않는다.
@@ -132,7 +133,7 @@ Finding마다 `F-001` 같은 ID, 파일·행, 발생 조건, 영향·심각도,
게시 단계는 [공통 Native review adapter](references/native-review.md)와 canonical `scripts/native_review.py`의 검증된 API를 사용한다. 매 실행마다 gate·게시·검증 bootstrap을 다시 생성하거나 지원하지 않는 CLI·legacy JSON schema를 추정하지 않는다. 기존 role-authenticated transport와 현재 보유한 Issue lease를 연결하고, durable journal을 유지해 timeout·partial 이후에는 POST 재시도 없이 readback만 수행한다. 정상적인 같은 작성자의 자동 supersession/retouch는 별도 opt-in 없이 기본 인정하되, exact before/after/new-review 대조로 정해진 `official`·`dismissed` 전이와 새 리뷰의 시각만 허용한다. 이전 작성자·본문·commit·판정·댓글과 다른 필드는 보존해야 하며 다른 작성자의 변경은 거부한다. `supersede_same_author`와 `retouch_dismissed`는 v1 호환 필드이지 이 정상 전이의 허가 스위치가 아니다. Commit `files` 순서 정규화만 `unordered_commit_files`와 별도 근거가 필요한 예외로 유지한다. 기존 plan의 bytes/fingerprint와 journal을 변경하지 않는다. Snapshot/plan schema, 실제 지원 interface 및 read-only replay와 live 게시의 차이는 참조 문서대로 확인한다. 게시 단계는 [공통 Native review adapter](references/native-review.md)와 canonical `scripts/native_review.py`의 검증된 API를 사용한다. 매 실행마다 gate·게시·검증 bootstrap을 다시 생성하거나 지원하지 않는 CLI·legacy JSON schema를 추정하지 않는다. 기존 role-authenticated transport와 현재 보유한 Issue lease를 연결하고, durable journal을 유지해 timeout·partial 이후에는 POST 재시도 없이 readback만 수행한다. 정상적인 같은 작성자의 자동 supersession/retouch는 별도 opt-in 없이 기본 인정하되, exact before/after/new-review 대조로 정해진 `official`·`dismissed` 전이와 새 리뷰의 시각만 허용한다. 이전 작성자·본문·commit·판정·댓글과 다른 필드는 보존해야 하며 다른 작성자의 변경은 거부한다. `supersede_same_author`와 `retouch_dismissed`는 v1 호환 필드이지 이 정상 전이의 허가 스위치가 아니다. Commit `files` 순서 정규화만 `unordered_commit_files`와 별도 근거가 필요한 예외로 유지한다. 기존 plan의 bytes/fingerprint와 journal을 변경하지 않는다. Snapshot/plan schema, 실제 지원 interface 및 read-only replay와 live 게시의 차이는 참조 문서대로 확인한다.
1. Exact PR 리뷰 요청은 진입 조건을 충족한 native review 하나의 게시만 승인한다. 단순 조사·역할 보고 요청은 write를 승인하지 않는다. `approve → APPROVED`, `request_changes → REQUEST_CHANGES`로 매핑하고 `blocked`는 게시 없이 보고한다. 1. Exact PR 리뷰 요청은 진입 조건을 충족한 native review 하나의 게시만 승인한다. 단순 조사·역할 보고 요청은 write를 승인하지 않는다. 게시 직전 진입 시 고정한 요청 참조를 재사용해 현재 요청·대상·승인 범위와의 일치를 재확인한다. 참조가 불명확하거나 불일치하면 게시하지 않는다. label이나 동일 문구의 다른 요청으로 대체하지 않는다. `approve → APPROVED`, `request_changes → REQUEST_CHANGES`로 매핑하고 `blocked`는 게시 없이 보고한다.
2. 게시 직전 Issue 계약, PR base·head, spec pass와 review baseline을 재확인한다. Drift면 중단한다. Body에는 결과 artifact의 요약과 아래 marker를 넣는다. 2. 게시 직전 Issue 계약, PR base·head, spec pass와 review baseline을 재확인한다. Drift면 중단한다. Body에는 결과 artifact의 요약과 아래 marker를 넣는다.
`<!-- gitea-agent:code-review/v1 owner=<owner> repository=<repository> issue=<issue-number> pr=<pull-number> head=<full-head-sha> contract-sha256=<digest> -->` `<!-- gitea-agent:code-review/v1 owner=<owner> repository=<repository> issue=<issue-number> pr=<pull-number> head=<full-head-sha> contract-sha256=<digest> -->`
3. 같은 author·head·계약의 canonical review가 하나 있고 body·최종 state가 같으면 readback 후 write 없는 no-op으로 종료한다. 중복·상충 결과 또는 PENDING이면 추가 게시하지 않는다. 3. 같은 author·head·계약의 canonical review가 하나 있고 body·최종 state가 같으면 readback 후 write 없는 no-op으로 종료한다. 중복·상충 결과 또는 PENDING이면 추가 게시하지 않는다.
@@ -17,6 +17,19 @@ class NativeJournalDocumentationTests(unittest.TestCase):
with self.subTest(path=str(path), token=token): with self.subTest(path=str(path), token=token):
self.assertIn(token, text) self.assertIn(token, text)
def test_journal_parent_preflight_precedes_expensive_execution(self):
text = SKILL.read_text()
entry = text.split('## 진입 조건', 1)[1].split('## 리뷰 관점', 1)[0]
for requirement in (
'고비용 리뷰·테스트 실행 전에',
'$HARNESS_ROOT/harness/.runtime', 'lstat', 'symlink',
'현재 review role 소유', '정확한 mode `0700`',
'읽기 전용', 'blocker', '운영자 승인', 'local-only',
'자동 chmod/chown', 'journal 이동', 'guard 완화',
'기존 생성 규칙', '게시 승인'):
with self.subTest(requirement=requirement):
self.assertIn(requirement, entry)
def test_no_caller_selected_journal_in_live_example(self): def test_no_caller_selected_journal_in_live_example(self):
text = REFERENCE.read_text() text = REFERENCE.read_text()
self.assertNotIn('result = native_review.publish(plan, baseline, api, lease, journal_directory)', text) self.assertNotIn('result = native_review.publish(plan, baseline, api, lease, journal_directory)', text)
@@ -189,6 +189,21 @@ class NativeReviewTests(unittest.TestCase):
m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'}) m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'})
self.assertNotIn('secret transport detail', str(error.exception)) self.assertNotIn('secret transport detail', str(error.exception))
self.assertEqual(api.posts, 1) self.assertEqual(api.posts, 1)
def test_publication_request_binding_is_independent_of_workflow_labels(self):
text = (ROOT / 'harness/agents/review/skills/gitea-code-review/SKILL.md').read_text()
entry = text.split('## 진입 조건', 1)[1].split('## 리뷰 관점', 1)[0]
publication = text.split('## Native review 게시와 재검토', 1)[1].split('### 재리뷰 수렴', 1)[0]
for rule in ('현재 요청의 원문', 'session/message ID', '독립적으로 대조',
'기존 workflow artifact', 'workflow label', 'run-id suffix',
'추론하지 않는다', '권한 근거가 아니다'):
with self.subTest(entry_rule=rule):
self.assertIn(rule, entry)
for rule in ('진입 시 고정한 요청 참조', '재사용', '현재 요청',
'불명확하거나 불일치하면 게시하지 않는다',
'label이나 동일 문구의 다른 요청으로 대체하지 않는다'):
with self.subTest(publication_rule=rule):
self.assertIn(rule, publication)
def test_empty_review_comments_no_header_contract_is_explicit(self): def test_empty_review_comments_no_header_contract_is_explicit(self):
text = (ROOT / 'harness/agents/review/skills/gitea-code-review/references/native-review.md').read_text() text = (ROOT / 'harness/agents/review/skills/gitea-code-review/references/native-review.md').read_text()
self.assertIn('### Empty review-comments readback without a count header', text) self.assertIn('### Empty review-comments readback without a count header', text)