에이전트 패치 내 새 테스트는 플레이크 프리즈를 상속할 수 없음
요약
본 문서는 테스트 패치와 관련된 '플레이크 프리즈'의 정확한 작동 원칙과 제약 사항을 설명합니다. 특히, 에이전트가 새로운 테스트를 추가하거나 수정할 경우, 기존의 플레이크 기록은 해당 변경사항을 측정하지 못하며, 오직 부모 매니페스트에서 제외된 ID만을 적격하게 간주함을 강조합니다.
핵심 포인트
- 플레이크 프리즈는 부모 커밋에 존재했던 테스트 식별자에만 적용됩니다.
- 패치가 새로운 테스트를 추가/수정하면 기존 기록은 해당 변경을 측정하지 못합니다.
- 적격한 범위는 부모 매니페스트에서 제외된 ID의 집합 연산으로 정의되어야 합니다.
- 프리즈 파일에는 test_id, parent_sha, assertion_hash 등 필수 필드가 요구됩니다.
플레이크 프리즈(flake freeze)는 부모 커밋에 이미 존재했던 테스트 식별자(test identities)에만 적용됩니다. 만약 에이전트 패치가 테스트를 추가하거나, 이름을 바꾸거나, 다시 작성한 다음, 그 새로운 식별자를 대상으로 프리즈 기록을 지정한다면, 게이트는 더 이상 해당 패치를 측정하는 것이 아닙니다. 대신, 패치 스스로에게 부여한 면제(exemption)를 측정하고 있는 것입니다.
이 규칙은 판단적인 호출이 아니라 집합 연산입니다. 부모 매니페스트에서 패치가 추가, 이름 변경, 다시 작성한 테스트 ID를 제외한 것이 유일하게 적격한 집합입니다. 그 외의 모든 것은 닫힌 상태로 유지됩니다. 새로운 프리즈 라인에 의존하는 녹색 실행(green run)은 계산되지 않습니다.
적격 행과 빨간색으로 남아 있어야 하는 카나리아
프리즈 기록은 이전에 관찰된 테스트에 대한 면제입니다. 이는 전체 스위트 단위의 무음 처리(mute)가 아닙니다. 이 제안에는 네 가지 필드, 즉 test_id, parent_sha, assertion_hash, 그리고 expires_at이 필요합니다. 검사기(checker)는 다음 경우 기록을 거부합니다. 모든 필드가 누락되었거나, test_id가 부모 매니페스트에 없거나, 또는 expires_at이 CI 클럭 대비 미래가 아닌 경우입니다.
패치 디프(diff)가 두 번째 입력값입니다. 테스트 함수의 추가된 파일, 이름 변경 대상, 그리고 제자리 재작성(in-place rewrites)은 이 변경 사항에 적격하지 않은 ID입니다. 이러한 ID 중 하나를 명시하는 프리즈는 다른 ID에 대한 이전 프리즈 라인이 유효하더라도 검사에 실패합니다.
| 신호 | 이 패치에서 적격 여부 | 필요한 조치 |
|---|---|---|
test_id가 부모 매니페스트에 있고, 디프에 의해 건드려지지 않았으며, 해시가 일치함 | 예 | 기존 기록 유지 |
| ... | ||
| 마지막 행은 증명 단계입니다. 프리즈 파일이 형식적으로는 잘 구성되었더라도, 러너(runner)가 이를 잘못된 ID에 적용한다면 여전히 너무 광범위할 수 있습니다. 레지스터(ledger)가 로드된 후에도 하나의 속성을 깨뜨리는 커밋된 변종(mutant)은 실패해야 합니다. |
예시적인 카운트이며, 측정 연구는 아닙니다: 12개의 속성 테스트를 가진 부모 매니페스트, 2개를 추가하고 1개를 재작성하는 패치, 그리고 3개 기록을 가진 프리즈 파일. 만약 그 기록 중 하나가 추가된 ID를 명시한다면, check는 2로 종료됩니다. 나머지 두 기록은 병합할 이유로 고려되지 않습니다. 부분적인 적격성은 통과가 아닙니다.
1. 부모 매니페스트 내보내기
부모 커밋(parent commit)에서 컬렉션을 실행해야 하며, 더티 워크트리(dirty worktree)에서는 안 됩니다. 매니페스트는 안정적인 테스트 ID 목록과 각 어설션 본문(assertion body)의 해시로 구성됩니다. 컬렉터가 타이밍 정보와 로컬 경로를 생략하는 경우 노드 ID는 허용됩니다.
git checkout --detach "$PARENT_SHA"
python -m pytest tests/properties --collect-only -q > /tmp/parent_collect.txt
python freeze_scope.py manifest --collect /tmp/parent_collect.txt --out parent_manifest.json
...
만약 collect-only 자체가 네트워크 호출이나 모델 엔드포인트에 의존한다면 중단해야 합니다. 동일 커밋에서 두 번의 컬렉트를 거쳐 매니페스트가 변경된다면 그것은 매니페스트가 아닙니다. 플래키(flakes)에 대해 논하기 전에 컬렉터를 수정하세요.
매니페스트는 CI 아티팩트 내에서 부모 SHA 옆에 저장해야 합니다. 에이전트 브랜치(agent branch)에서 재생성하여 베이스라인(baseline)이라고 부르지 마세요. 이미 패치를 포함하고 있는 베이스라인은 자체 발행된 면제(self-issued waiver)를 감지할 수 없습니다.
2. 패치 내 테스트 ID 분류하기
diff classifier는 어떤 테스트가 좋은지 결정하지 않습니다. 단지 ID를 added(추가됨), renamed_to(이름 변경됨), rewritten(재작성됨), 또는 untouched(변경 없음)로 레이블링할 뿐입니다.
git diff --name-status "$PARENT_SHA"...HEAD -- tests/properties > /tmp/name_status.txt
git diff -U0 "$PARENT_SHA"...HEAD -- tests/properties > /tmp/test_diff.patch
python freeze_scope.py classify \
...
classify는 의도적으로 보수적입니다. 함수 본문을 변경하는 덩어리(hunk)는 해당 함수를 rewritten으로 표시합니다. 재작성된 ID는 파일 이름이 동일하게 유지되더라도 같은 패치 내에서 freeze를 상속할 수 없습니다. 이는 이름을 유지하고 어설션을 삭제하는 조용한 편집을 차단합니다.
변경 없음(Untouched) ID가 계속 적격성을 가지려면 해당 어설션 해시가 부모 매니페스트와 여전히 일치해야 합니다. freeze 기록은 파일 경로에 붙는 떠다니는 권한이 아닙니다. 그것은 하나의 ID와 하나의 어설션 본문에 붙습니다.
3. 자체 발행된 freeze 라인 거부하기
check는 flake_freeze.json을 로드하고 적격 집합(eligible set)을 계산합니다. 노트북의 시간대 설정이 기록을 늘리는 것을 막기 위해 명시적인 시간을 전달하세요.
python freeze_scope.py check
--manifest parent_manifest.json
--patch-tests patch_tests.json
...
제안된 종료 코드:
0: 모든 freeze 라인이 적격 집합에 속하며 만료되지 않았음을 의미합니다.2: freeze 라인이 추가, 이름 변경 또는 재작성된 ID를 지정했음을 의미합니다.3: 단언(assertion) 해시 불일치 또는 알 수 없는 부모 ID를 의미합니다.4: 만료된 기록이 활성으로 제시되었음을 의미합니다.
어떠한 종료 코드도 없다는 것은 패치가 정확하다는 뜻입니다. 0만은 해당 변경 사항이 도입하거나 재작성한 테스트를 waiver 파일이 커버하지 않음을 의미할 뿐입니다. 원장(ledger) 외부의 속성 실패는 여전히 빌드를 실패시킵니다.
4. 카나리 적용 후, Red 요구하기
에이전트 작업 공간 밖에 mutants/ 아래에 하나의 돌연변이체(mutant)를 유지합니다. 이 돌연변이체는 테스트 스위트가 강제한다고 주장하는 단일 속성을 위반해야 합니다. 프로덕션 코드에서 하한 검사(lower-bound check)를 삭제하는 것만으로 충분합니다. 테스트를 변형시키지 마십시오. 테스트를 수정하는 돌연변이체는 범위 확인을 동작 확인과 혼동시킵니다.
임시 사본에 돌연변이체를 적용합니다. 그런 다음 러너에게 원장이 보이도록 스위트를 실행합니다.
python freeze_scope.py apply-mutant
--mutant mutants/drop_lower_bound.py
--workdir /tmp/canary-tree
...
예상 결과: 0이 아닌 pytest 상태입니다. 만약 pytest가 0으로 종료되면, 원장이나 러너가 해당 속성을 무시했다는 뜻입니다. 병합을 중단합니다. 카나리 실패에 대해 freeze 라인을 추가하여 이를 수리하지 마십시오. 이 카나리는 플레이크 샘플이 아닙니다.
동일한 명령어를 두 번째 깨끗한 작업 공간에서 반복합니다. 두 종료 코드 모두 0이 아니어야 합니다. 불일치는 환경 결함입니다. 속성이 선택 사항이 되었다는 증거가 아닙니다.
5. 참조 검사기 (Reference checker)
아래 모듈은 제안입니다. 이 노트의 프로덕션 스위트에 대해 실행된 적이 없습니다. 모델을 호출하지 않습니다. 위의 bash 플래그에 대한 인자 구문 분석은 이러한 함수들을 감쌀 수 있습니다. 집합 연산(set math) 부분이 결정론적이어야 하는 부분입니다.
#!/usr/bin/env python3
"""제안: scope flake freeze를 부모 테스트 ID에 연결합니다."""
...
assertion_hash는 나중에 오는 명령어가 매니페스트(manifest)와 동일한 방식으로 본문(body)에 스탬프를 찍을 수 있도록 포함됩니다. 체커(checker)는 저장된 해시 값을 비교합니다. 이것이 더 짧은 경계가 충분히 가깝다고 결정하는 것은 아닙니다.
최소한의 원장 행(ledger row)은 다음과 같습니다:
{
"records": [
{
...
플레이스홀더 해시를 매니페스트 값으로 대체하십시오. 수동으로 작성된 해시는 불일치이며, 불일치는 종료 코드 3을 의미합니다. 샘플 날짜는 필드 형식이지 권장되는 포기(waiver) 길이가 아닙니다.
초안 모델과 두 번째 작업 공간의 활용처
공개 고지: 이 문서는 MonkeyCode의 제품 홍보 활동의 일환으로 작성되었습니다.
MonkeyCode의 무료 모델 접근은 하나의 초안 작업에 적합합니다. 이를 사용하여 후보 돌연변이(candidate mutant) 또는 후보 속성 진술(candidate property statement)을 텍스트로 제안하십시오. 체커는 해당 텍스트를 권위로 읽지 않습니다. 실패가 불안정하다고 언급하는 완료(completion)는 원장 행을 생성하지 않습니다. 엔드포인트가 빈 본문이나 관련 없는 제안을 반환하면, 이를 무시하고 부모 매니페스트를 유지하십시오. 집합 차이(set difference)에는 모델이 필요하지 않습니다.
무료 서버 옵션은 약한 게이트(gate)가 아니라 두 번째 작업 공간입니다. 로컬 캐나리(canary)가 실패하면, 동일한 돌연변이와 동일한 원장을 그곳에서 실행하십시오. 두 실행 모두 0이 아닌 상태를 보여야 합니다. 로컬 통과에 서버 실패는 노트북 환경이 오염되었음을 의미합니다. 로컬 실패에 서버 통과는 서버 트리가 해당 돌연변이나 원장을 누락했음을 의미합니다. 어느 경우에도 새로운 동결선(freeze line)을 승인하지 않습니다.
가용성은 할당량, 하드웨어 프로필 또는 영속성 주장(permanence claim)이 아닙니다. 그러한 세부 사항은 이 문서에서 확립되지 않았습니다. 무료 서버가 다운되면, 두 개의 새로운 가상 환경(virtualenvs)에서 캐나리를 두 번 실행하고 두 종료 코드를 기록하십시오. 격리(Isolation)가 요구사항입니다. 호스팅된 작업 공간은 두 번째 사본을 얻는 한 가지 방법일 뿐입니다.
제한 사항
부모 매니페스트가 오염된 트리에서 생성되었다면 이 접근 방식은 열려 있게 실패합니다. PARENT_SHA로부터만 재계산하십시오.
Git의 이름 변경 감지 기능은 이름이 변경된 후 삭제되었다가 새 이름으로 다시 추가된 테스트를 놓칩니다. 이름 변경 임계값(rename threshold) 아래에서 발생한 경우입니다. 일치하지 않는 새 ID는 추가됨(added)으로 처리됩니다. 이로 인해 일부 합법적인 이동(move)이 차단될 수 있습니다. 차단된 이동은 자체 발행 면제(self-issued waiver)보다 비용이 적게 듭니다.
카나리아(canary) 테스트는 여전히 실패하는 변이체(mutant)가 하나 있다는 것만 증명할 뿐입니다. 이 테스트가 얼마나 많은 버그를 잡아낼 수 있는지 추정하지는 못합니다. 더 광범위한 변이 점수(mutation score)는 자체적으로 발표된 방법론이 필요합니다. 본 메모에는 해당 방법론이 포함되어 있지 않으며, 점수를 보고하지도 않습니다.
런타임에서 생성된 테스트 ID는 이 원장(ledger)을 사용할 수 없습니다. 먼저 ID를 안정화해야 합니다. 스냅샷 승인만 있고 속성(property)이 없는 경우 이 접근 방식을 건너뛰십시오. 패치 내부의 스냅샷 업데이트는 재작성된 테스트처럼 보이므로, 검사기(checker)가 이를 차단할 것입니다. 해당 결과는 스냅샷이 면제 경로가 아닐 때에만 적절합니다.
실패하는 속성을 녹색으로 유지하기 위해 이 검사기를 라이선스로 사용하지 마십시오. 만료된 행(expired rows), 해시 불일치(hash mismatches), 그리고 카나리아 통과(canary passes)는 하드 스톱(hard stops)입니다. 이는 동일한 커밋에서 재정의할 수 있는 주석이 아닙니다.
누가 건너뛰어야 하는가: 부모 커밋 매니페스트(parent-commit manifest)가 없는 팀, ID에 타임스탬프를 포함하는 테스트 스위트, 그리고 범위 게이트(scope gate)보다 통계적 플레이크율(statistical flake rate)이 필요한 검토입니다. 이 절차는 더 좁은 질문에 답합니다. 이 패치가 방금 작성한 테스트를 면제했는가?
마무리
빨간색 상태였던 것이 플래키(flaky)했는지 논쟁하기 전에 적격 집합(eligible set)을 계산하십시오. 그런 다음 원장(ledger)을 로드하고 카나리아가 빨간색 상태를 유지하도록 요구하십시오. 만약 MonkeyCode free-server 작업 공간이 이미 사용 가능하다면, 이를 두 번째 복사본으로 사용하고 두 종료 코드(exit codes)를 CI 로그에 모두 저장하십시오. 면제 파일은 이 패치가 방금 작성한 테스트를 볼 수 없을 때만 유효합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기