diff --git a/harness/agents/review/skills/gitea-code-review/SKILL.md b/harness/agents/review/skills/gitea-code-review/SKILL.md index eb01e8c..99f7e1d 100644 --- a/harness/agents/review/skills/gitea-code-review/SKILL.md +++ b/harness/agents/review/skills/gitea-code-review/SKILL.md @@ -90,6 +90,18 @@ Cleanup이 미확인이라는 이유로 완료한 검토·전체 테스트·Nati 안전한 대여가 불가능하면 CI·기존 근거를 사용하고 직접 실행 불가 사유를 보고한다. 필수 검증 근거가 부족하면 승인하지 않는다. 독립성은 reviewer의 판단과 직접 확인에 있으며 별도 checkout 자체가 필수는 아니다. +### 장시간 suite의 timeout evidence recipe + +이미 승인된 reviewer 직접 실행 중 장시간 suite가 필요한 경우에만 다음 recipe를 기존 bounded execution·artifact 계약에 연결한다. 새 runner·streaming framework·자동 retry·schema를 추가하지 않으며 pytest를 강제하지 않는다. + +- 실행 시작부터 승인된 artifact 경로에 해당 run-id·source revision·명령·실행 환경·유한한 deadline과 연결된 로그를 보존한다. 기존 승인된 capture/file-output 수단을 사용하고 redaction·권한·출력 한도·deadline을 유지한다. 단순 stdout 파일 redirect만으로 이 보호가 구현됐다고 주장하지 않는다. 안전한 보존을 확인할 수 없으면 실행을 시작하지 않고 evidence_gap으로 보고한다. 기존 source·plan·output binding과 no-clobber 계약을 유지한다. +- 결과에는 exit status, timeout·출력 잘림, 관측 시간, 실제 종료·reap·process-group cleanup과 미확인 상태를 구분한다. Tool timeout이나 로그 마지막 행은 process 종료의 증거가 아니다. Running/unknown이면 suite를 자동 재실행하지 않는다. 기존 bounded termination·cleanup 절차를 적용하고 필수 검증 미완료는 미검증/partial로 남긴다. 중간 PASS 출력이나 일부 JUnit을 전체 suite 성공으로 승격하지 않는다. +- Timeout 로그·실패 결과를 덮어쓰지 않는다. 필요한 재실행은 이전 실행과 관련 작업의 실제 종료·cleanup을 확인하고 기존 승인 범위 및 lease·head·source 검사를 다시 통과한 뒤 새 run-id로 수행한다. 종료·cleanup을 확인할 수 없으면 재실행하지 않는다. 무조건적인 전체 재실행이나 자동 retry 권한을 추가하지 않는다. +- 다음 유한한 budget은 동일 입력 또는 비교 가능한 source·환경·fixture의 관측 시간과 명시한 여유 근거로 설정한다. Timeout 시점은 완료 소요시간이 아니라 하한임을 기록하고, 필요하면 기존 승인 범위의 bounded 진단 근거를 사용한다. 비교가 불가능하면 불확실성을 명시하고 임의의 시간 증가를 측정된 개선이라고 보고하지 않는다. Budget 증가로 원래 timeout 기록을 삭제하거나 pass/fail을 바꾸지 않는다. +- JUnit은 test runner가 이미 지원하고 이번 검증에 필요한 경우에만 run별 승인된 artifact 경로로 보존한다. Timeout 중 완성된 JUnit이 생성된다고 가정하지 않는다. 누락·malformed·truncation은 증거 한계로 기록하며 JUnit 유무가 실제 exit·종료·전체 collection 검증을 대신하지 않는다. 제품 설정·plugin·dependency를 바꾸거나 JUnit을 필수 공통 format으로 강제하지 않는다. + +이 문서 계약은 실행 계획과 보고의 지침이며 실제 streaming·JUnit 생성·process cleanup 또는 agent 준수를 검증하지 않는다. 원래 후보의 범용 실행 자동화·suite별 streaming/JUnit 구현 전체가 완료됐다고 주장하지 않고 부분 완료로 유지한다. + ### Dependency provenance와 probe JSON framing 기존 승인 환경을 사용할 수 없고 별도로 승인된 환경 준비가 필요한 경우, 실제 interpreter 경로·버전, 검증에 관련된 실제 resolved dependency 버전, source revision과 dependency/lockfile·설정 출처를 기존 evidence의 환경·command·확인 한계 기록에 결합한다. 실제 설치 상태를 조회한 값과 요구사항의 버전 범위를 구분하고, 실패한 resolution·collection의 command·exit code·안전한 bounded output을 보존한다. Baseline API/dependency 호환성은 기존 승인 환경·검증된 기록 또는 승인된 read-only 근거로 비교하며 제품 회귀, upstream/baseline drift와 reviewer 환경 오류를 구분한다. 비교할 수 없으면 확인 한계를 보고하고 필수 검증 부족은 기존 미검증/blocked 기준을 적용한다. 이 지침은 새 venv·checkout 생성이나 임의 설치·pinning 권한을 부여하지 않으며 source·requirements·lockfile을 바꾸거나 새 dependency manager·claim schema를 만들지 않는다. diff --git a/harness/tests/review/test_timeout_evidence_recipe_docs.py b/harness/tests/review/test_timeout_evidence_recipe_docs.py new file mode 100644 index 0000000..0537f34 --- /dev/null +++ b/harness/tests/review/test_timeout_evidence_recipe_docs.py @@ -0,0 +1,22 @@ +"""Reviewer timeout recipe documentation only; not a runner/runtime test.""" +from pathlib import Path +import unittest + +SKILL = Path(__file__).resolve().parents[3] / 'harness/agents/review/skills/gitea-code-review/SKILL.md' +HEADING = '### 장시간 suite의 timeout evidence recipe' + +class TimeoutEvidenceRecipeDocumentationTests(unittest.TestCase): + def test_conditional_recipe_preserves_evidence_without_automatic_retry(self): + text = SKILL.read_text() + self.assertIn(HEADING, text) + section = text.split(HEADING, 1)[1].split('\n### ', 1)[0].split('\n## ', 1)[0] + for rule in ('장시간 suite', '실행 시작부터', '승인된 artifact 경로', + 'source revision', 'run-id', '명령', 'deadline', 'redaction', + '출력 한도', 'exit status', 'timeout', '실제 종료', 'cleanup', + '새 run-id', '덮어쓰지 않는다', '자동 재실행하지 않는다', + '유한한 budget', '관측 시간', '여유 근거', '동일 입력', + 'JUnit', '이미 지원', '완성된 JUnit', 'pytest를 강제하지', + '새 runner', 'streaming framework', '미검증', '문서 계약'): + with self.subTest(rule=rule): + self.assertIn(rule, section) + self.assertIn('lease·head·source', section)