작은 C++ 프레임 파서에 대한 화요일 리뷰를 상상해 봅시다. 후보 패치는 골든 파일과 동일한 페이로드 길이를 출력하고, 정상 경로(happy-path) 바이너리는 0으로 종료됩니다. 리뷰어들은 이를 병합합니다. 이후 오래된 noexcept 약속을 기반으로 이미 컴파일된 다운스트림 호출자가 짧은 프레임에서 중단되는 상황이 발생합니다.
출력문에는 예외에 대한 언급이 전혀 없었습니다. 녹색 실행(green run)에서도 파서에 빈 버퍼가 공급된 적이 없습니다. 바로 이 간극을 아래의 레저가 포착하기 위해 만들어졌습니다.
일치하는 출력물이 검증하지 못하는 것들
정상 경로 테스트 케이스는 관찰 가능한 텍스트만을 확인합니다. 이는 호출자가 이미 컴파일된 예외 명세(exception specification)를 확인하지 않습니다. 이들은 서로 다른 계약이며, 패치는 하나를 만족시키면서 다른 하나를 깨뜨릴 수 있습니다.
헤더에서 noexcept를 제거하는 것은 정상 출력을 변경하지 않을 수 있습니다. 선언에는 noexcept를 유지하면서 본문에서 예외를 던지는 것도 마찬가지일 수 있으며, 이는 잘못된 입력이 std::terminate를 호출할 때까지 지속됩니다. 이러한 누락은 골든 텍스트 파일 옆에 조용히 존재합니다.
워크플로우는 단일 시도 ID로 이 두 가지 누락을 모두 등급화합니다. 그러나 이것이 미정의 동작(undefined behavior), 데이터 경쟁(data races), 또는 메모리 누수(leaks)가 없음을 증명한다고 주장하지는 않습니다.
계약과 샘플 분리하기
제안된 트리는 골든 파일을 모델이 덮어쓸 수 있는 디렉토리 외부에 유지합니다. 각 샘플은 새로운 시도 폴더를 받습니다. 이전 샘플의 오브젝트 파일이 다음 링크로 새어 나가지 않습니다.
golden/frame.hpp는 마지막으로 검토된 선언입니다. 시도 포함 파일(attempt include file)은 이 헤더의 복사본으로 시작하여, 프롬프트가 허용한 수정 사항만 받습니다. 주석만 다른 차이는 실패를 의미하지 않습니다. 소비자 트랜슬레이션 유닛(consumer translation unit)이 해당 선언이 여전히 예외를 던지지 않는지 결정합니다.
tests/contract_tu.cpp는 후보 헤더를 포함합니다. tests/happy.cpp와 tests/malformed.cpp는 후보 오브젝트를 링크하고 한 줄을 출력합니다. malformed 프로그램은 계약의 런타임 절반입니다: 종료 코드 0과 구분자(sentinel) 0 뒤에 개행 문자가 있다는 것은 중단 없이 짧은 프레임이 보고되었음을 의미합니다.
이 레이아웃은 제안과 실행되지 않은 예시를 포함합니다. 이 레이아웃은 -std=c++20을 수용하는 POSIX g++ 환경을 전제합니다. Windows에서는 숫자 abort 코드가 다르며, 0이 아닌 상태는 여전히 해당 열에서 실패합니다.
스티치드(stitched)된 성공 빌드를 거부하는 단계들
attempts/001/include, attempts/001/src, 그리고 attempts/001/build를 생성합니다. 후보(candidate)를 적용하기 전에 golden/frame.hpp를 시도 포함 트리(attempt include tree)에 복사합니다.
- 해당 샘플의
payload.cpp를 attempts/001/src 아래에 배치합니다. 이전 샘플이 이미 실패했다면 이전 시도 디렉터리는 건드리지 않습니다.
- 후보(candidate)를 컴파일하여
payload.o를 만듭니다. 0이 아닌 상태는 patch_compile에서 실패하며, 이 시도에 대해서만 승진을 차단합니다.
tests/contract_tu.cpp를 후보 헤더와 새로운 오브젝트 파일과 함께 컴파일합니다. static_assert가 실패하면 noexcept_contract에서 실패합니다.
tests/happy.cpp를 링크하고 실행한 후, 끝에 붙는 개행 문자(trailing newline)를 포함하여 표준 출력(stdout)을 golden/happy.out과 바이트 단위로 비교합니다.
tests/malformed.cpp를 링크하고 짧은 시간 초과(timeout)와 함께 실행하며, 종료 코드 0과 golden/malformed.out이 필요합니다. 시그널(signal), 이스케이프된 예외(escaped exception), 또는 시간 초과는 malformed_run에서 실패합니다.
ledger.jsonl에 JSON 객체 하나를 추가합니다. 시도 ID, payload.cpp의 해시, 그리고 모든 열을 포함해야 합니다. 해당 객체가 필요한 모든 열을 'ok'으로 표시할 때만 승진(promote)됩니다.
- 실패한 행은 유지합니다. 나중에 샘플이 자신의 열에서 통과해야 합니다. 시도 1에서의 stdout 통과와 시도 2에서의 계약 통과는 증거가 아닙니다.
스티치(stitch)는 이 레저(ledger)가 막아내는 검토 습관입니다. 재시도된 샘플은 도구가 가장 최신 녹색 라인만 출력하고 실제로 계약을 지켰던 시도는 폐기할 때 진전처럼 보입니다.
선언, 고정 환경(fixtures), 그리고 그레이더
검토된 계약(Reviewed contract)은 golden/frame.hpp로 저장됩니다:
#pragma once
#include <string_view>
#include <cstddef>
...
tests/contract_tu.cpp에서 소비자 검사(Consumer check)를 수행합니다. noexcept 연산자는 컴파일러가 볼 수 있는 선언을 확인합니다. 이는 본체(body)를 실행하지 않습니다.
Happy path는 tests/happy.cpp에서 테스트됩니다. 2바이트 버퍼를 사용하면 hex escape가 발생하지 않아 두 번째 바이트 값이 계속 4로 유지됩니다.
#include "frame.hpp"
#include <iostream>
#include <string_view>
...
tests/malformed.cpp의 Short-frame path는 다음과 같습니다:
#include "frame.hpp"
#include <iostream>
...
헤더와 일치하면서 예외를 발생시키지 않고 짧은 프레임을 보고하는 본문입니다:
#include "frame.hpp"
std::size_t payload_size(FrameView frame) noexcept {
...
해당 함수는 파서가 아니라 하네스(harness)를 위한 고정된 구조체(fixture)입니다. 2바이트 입력의 경우, golden/happy.out에는 문자 '4'와 개행 문자가 포함되어 있습니다. 빈 뷰(view)의 경우, golden/malformed.out에는 0과 개행 문자가 포함되어 있습니다.
만약 정의가 noexcept로 표시되었는데도 예외를 발생시키면, 프로그램은 컴파일되고 이후 std::terminate를 호출합니다. Python의 subprocess 모듈은 POSIX 시그널을 음수 반환 코드로 보고하며, 종종 SIGABRT의 경우 -6입니다. 그레이더(grader)는 모든 0이 아닌 코드를 실패한 열로 취급하므로, 134나 -6에 대해 특별히 처리하지 않습니다. 만약 후보자가 헤더와 정의에서 noexcept를 함께 제거하면, 두 텍스트 고정 구조체 모두 일치함에도 불구하고 계약(contract) 번역 단위가 실패합니다.
실행되지 않은 그레이더는 golden/과 tests/ 옆에 grade_attempt.py로 저장됩니다:
#!/usr/bin/env python3
"""미실행 예제. 모든 열이 통과될 때 한 번의 시도만 승격 가능합니다."""
import hashlib
...
첫 번째 시도를 생성하고 하나의 레저 행(ledger row)을 추가하는 명령어는 다음과 같습니다. 이는 골든 헤더와 세 개의 테스트 파일이 저장된 후에 실행됩니다:
mkdir -p attempts/001/include attempts/001/src
cp golden/frame.hpp attempts/001/include/frame.hpp
cp candidates/001/payload.cpp attempts/001/src/payload.cpp
...
두 번째 샘플은 attempts/002에 복사하는 것을 반복합니다. 이는 이미 001에 작성된 행을 수정하지 않습니다.
행 읽는 방법
| Row shape | 의미 | 승격 (Promote) |
|---|
| All six required columns ok on this attempt | 동일 샘플이 선언을 보유했으며 두 실행 모두 성공함 | 검토 대상 후보이며, 자동 병합은 아님 |
| ... | | |
| 승격(Promotion)은 레저 프로세스가 0으로 종료됨을 의미합니다. 이는 유지 관리자가 diff를 건너뛰어야 한다는 것을 의미하지 않습니다. 페이로드 해시(payload hash)는 검토자가 디스크에 있는 파일이 채점된 파일임을 확인할 수 있게 합니다. | | |
noexcept_contract 행의 실패는 보통 컴파일러의 static-assertion 메모로 끝납니다. patch_compile 행의 실패는 종종 예외 사양 불일치(exception-specification mismatch)를 명시하며, 이는 헤더와 정의가 서로 달랐음을 의미합니다. 음수 반환 코드를 가진 malformed_run의 실패는 종료 경로입니다.
모델에게 다른 샘플을 요청하기 전에 이 순서를 읽어보십시오. 다른 샘플은 잘못된 헤더 복사본을 기준으로 채점된 레저를 수정하지 못합니다.
샘플 소스 자체가 등급(grade)이 아니다
공개 정보: 본 문서는 MonkeyCode의 제품 홍보의 일환으로 작성되었습니다.
MonkeyCode의 무료 모델 액세스는 후보 payload.cpp와 프롬프트가 허용하는 경우 대체 헤더를 제공할 수 있습니다. 무료 서버 옵션은 attempts/와 ledger.jsonl을 저장할 수 있으며, 이는 실패한 행들이 단일 노트북 디스크에 쌓이는 것을 방지하고 프로덕션 러너로부터 분리합니다. 이 제품이 패치를 채점하는 것이 아닙니다. 스크립트가 합니다.
현재 제한 사항은 기억해야 할 캠페인 문구가 아니라 제품 문서에 포함되어야 합니다. 본 가이드라인은 토큰 허용량, 모델 이름, 하드웨어 사양, 보존 기간, 그리고 무료 티어가 고정된다는 약속을 언급하지 않습니다. 공유된 검토가 해당 작업 공간에 의존하기 전에, 운영자는 컴파일러 버전, 디스크 규칙, 그리고 개인 소스 코드가 허용되는지 여부를 확인해야 합니다.
시도 트리에서 키와 라이브 패킷 캡처는 제외하십시오. 위 두 개의 고정(fixture)만으로 열들을 테스트하기에 충분합니다. 서버 컴파일러와 노트북 컴파일러가 다를 경우, 시도가 있을 때 g++ --version의 첫 줄을 기록하십시오. 왜냐하면 한 드라이버에서 채점된 행은 다른 드라이버에 대한 약한 증거이기 때문입니다.
스크립트는 모델을 호출하거나 서버에 로그인하지 않습니다. 네트워크 단계를 그레이더(grader) 외부로 유지하면 API의 시간 초과가 계약 통과(contract pass)로 기록될 수 없습니다.
이 게이트를 실행해서는 안 되는 경우
선언 체크는 본문에는 무관합니다. 잘못된 프로세스는 하나의 입력을 다룰 뿐, 입력 공간 전체를 다루지 않습니다. 메모리 누수(Leaks)와 경쟁 조건(races)은 자체적인 게이트가 필요하며, 5초의 시간 초과는 사람이 해당 행을 읽기 전까지 느린 디스크와 교착 상태(deadlock)를 혼동하게 만듭니다.
선택된 함수들이 예외를 발생시키는 것을 허용하는 코드베이스는 그러한 함수들에 static_assert(noexcept(...))를 목표로 삼아서는 안 됩니다. 진실된 모든 수정은 실패할 것입니다. 공식적인 증명이나 미정의 동작(undefined behavior)에 대한 전체 감사를 필요로 하는 팀들은 여섯 개의 프로세스 열보다 더 무거운 도구가 필요합니다.
공개 계약을 완화하는 것이 목적인 변경 사항이 이전 골든 헤더(golden header)에 부딪혀서는 안 됩니다. golden/frame.hpp를 업데이트하고 검토된 커밋에서 예상 파일들을 샘플링한 후, 새로운 계약에 대해 테스트해야 합니다. 승인된 API 수정 작업을 차단하는 것은 프로세스상의 실수입니다.
이 예제는 본문에서 실행되지 않았습니다. 스크래치 트리(scratch tree)가 위의 레이아웃과 다르다면 포함 경로를 조정할 것으로 예상하십시오.
실패한 시도를 디스크에 유지하기
스크래치 리포지토리와 하나의 후보 파일만 있으면 꿰매진 녹색 빌드가 0이 아닌 값으로 종료되는 것을 확인할 수 있습니다. 이미 MonkeyCode 작업 공간을 사용하는 독자들은 해당 시도 디렉터리들을 무료 서버 옵션에 보관한 다음, 그 작업 공간을 검토 저장소로 취급하기 전에 현재 문서를 확인하여 제한 사항을 알아야 합니다.