Anvil에는 심사 패킷의 계약이 둘 있어. 봉인된 레일에서는 열두 번째 불변식이 엄격해. 심사위원은 과제서와 결과물과 심사 지침만 받고, 저자의 정체나 자리의 과정이나 변경 메모나 대화 이력이나 재시도 횟수나 걸린 시간은 절대 못 받아.
Open Run은 경로와 결과를 함께 판단하도록 일부러 설계됐어. 패킷에는 과제서와 기준, 보관된 제출 나무, 선택 경로 보고서, 진행 방식 공개, 똑같이 재현되는 증거 묶음이 들어가고, 저장소를 쓰는 과제라면 얼린 기준점의 명세도 들어가. 이 보고서들은 금지된 계측값이 아니라 선언된 심사 대상의 일부야.
그래도 사적인 yard, 하네스 대화 기록, 엔진의 계측값, 운영자의 논평, 정직한 가면-조합 대응은 경계 밖이야. 심사위원이 볼 글에는 눈에 띄는 단서만 가볍게 걷어내고 결과물 파일은 바이트 그대로 둬. 심사위원은 참가자를 알아내려 하지 말라는 지침을 받고, 참가 전에는 공개 요약만 봐.
모든 심사위원은 저장소 사본과 Anvil이 만든 같은 투영을 읽어. 화면 갈무리나 실행기 출력이 없을 수도 있고, 패킷은 그 사실을 밝혀야 해. 대신 작업자가 만든 갈무리나 주장을 몰래 끼워 넣으면 안 돼.
패킷을 말하기 전에 레일부터 이름 붙여. 봉인된 심사는 과정을 빼고, Open Run은 선언된 보고서와 진행 방식을 넣지만, 사적인 생산 잔여물은 여전히 빼.
Code
레일마다 다른 허용 목록, 함께 쓰는 금지 목록·python
SEALED = {"brief", "artifacts", "judge_instructions"}
OPEN = SEALED | {"criteria", "submission_tree", "choice_path_report",
"conduct_disclosure", "evidence_pack", "base_manifest"}
NEVER = {"private_yard", "harness_transcript", "engine_telemetry",
"operator_commentary", "identity_mapping"}
def build_packet(rail, available):
allow = SEALED if rail == "sealed" else OPEN
packet = {k: v for k, v in available.items() if k in allow}
leaked = set(available) & NEVER & set(packet)
assert not leaked, f"경계 밖 항목이 샜다: {sorted(leaked)}"
# 없는 건 조용히 빠지는 게 아니라 '없음'으로 밝힌다.
packet["absent"] = sorted(allow - set(packet))
return packet
available = {"brief": "…", "artifacts": ["A"], "judge_instructions": "…",
"choice_path_report": "…", "private_yard": "/yards/a"}
s = build_packet("sealed", available)
o = build_packet("open", available)
assert "choice_path_report" not in s # 봉인 레일은 과정을 빼고
assert "choice_path_report" in o # 열린 레일은 선언된 심사 대상
assert "private_yard" not in s and "private_yard" not in o # 둘 다 절대 금지
print(sorted(o), "| 없음:", o["absent"])