나중에 해결됐다는 이유로 앞선 실패를 성공으로 덮으면 무엇을 고쳤는지 설명할 수 없어진다. 더 위험한 것은 같은 입력이 이미 전달됐는지 알 수 없게 되는 일이다. 실제 AI 서비스 실증 기록에서는 실패한 시도와 그 뒤 읽기·수정·시험을 다른 사건으로 이어 붙였다.

2026-10-27 보완에서는 같은 실행 구간의 문서 읽기 근거와 재시작 뒤 복원의 차이, 새 체크포인트와 원래 실패 기록의 관계를 후속 소스에 맞춰 덧붙였다.

클라이언트 실패와 서버 효과가 함께 남을 수 있다

과거 ZIP 첨부는 실제 사이트에 올라갔지만 클라이언트 선택 증명에서 오류가 나 needs_review가 남았다. 이후 화면을 읽어 첨부를 확인했고 같은 파일을 다시 올리지 않았다. 실패 기록은 클라이언트가 어떤 증명을 얻지 못했는지 남기는 자료다.

이때 화면에서 첨부를 봤다고 과거 실행의 모든 검증과 최종 기록이 자동 성공이 되는 것은 아니다. 후속 읽기의 시간과 관찰을 원래 시도에 연결한다. 전송 여부와 상태 기록이 어긋난 사실을 보존해야 다음 담당자가 단순 재시작을 선택하지 않는다. 잘못된 빨간 표시를 지우는 편의보다 중복 효과를 피할 근거를 남기는 편이 중요했다.

작성된 초안을 읽는 것과 다시 입력하는 것은 다르다

여러 줄 입력은 문단 판정 오류 때문에 전송 전에 멈춘 기록이 있다. 그때 이미 작성된 시험 초안의 정확한 내용을 읽기 대조하고 한 번 보냈다. 같은 내용을 다시 채워 넣지 않아도 다음 확인으로 나갈 수 있었다. 빈 줄과 CSS 간격을 혼동한 원인은 별도 수정으로 남았다.

수정 뒤 실제 페이지 API로 여러 줄 입력과 새 답변을 확인한 결과도 따로 있다. 이것을 이전 플러그인의 새 실행 성공이라고 옮기지 않았다. 직접 API 경로와 플러그인 경로의 이름이 비슷해도 실행 번호와 검증 순서가 다르다. 어느 경로가 실제로 끝났는지 알아야 다음 회귀 시험의 대상을 고를 수 있다.

수정은 원인에, 후속 시험은 바뀐 코드에 연결한다

파일 전송의 선택 증명을 고친 뒤에는 독립 HTTP 수신 시험으로 바이트와 새 선택 조건을 확인했다. 최종판으로 실제 ChatGPT에 ZIP을 다시 올린 것은 아니었다. 최종 코드의 로컬 근거와 이전 버전의 실사이트 효과를 나란히 두고 둘의 차이를 남긴다.

Gemini 기록에서도 메뉴 항목 인식, 축약 첨부 카드, 편집 창 오인과 거절 분류를 각각 원인과 시험에 연결했다. 한 군데 고쳤다는 이유로 전체 기능의 실패를 한꺼번에 해제하지 않는다. 후속 통과는 무엇을 바꿔서 어떤 반례가 달라졌는지 설명할 때 가치가 있다. 통과한 수만 적으면 원래 실패와의 관계가 사라진다.

오래 기다리다 멈춘 기록도 성공으로 세탁하지 않는다

생성이 길어 실행기 대기를 취소한 경우에도 사이트의 생성을 중지하거나 같은 요청을 다시 보내지 않고 읽기로 결과를 찾은 기록이 있다. 나중에 매체를 받았다고 취소한 실행기의 종료 상태까지 succeeded로 고치지는 않는다. 생성 결과와 관찰 프로그램의 수명은 다르다.

실제 Gemini 기록도 실패·취소를 성공으로 덮지 않았다고 명시한다. 내부 권한 거절을 만났다면 작업 소유권을 우회하지 않고 기존 실행을 정리한 뒤 읽는다. 이 절차는 원래 효과의 확인이지 새로운 생성의 성공이 아니다. 실행기의 상태와 외부 매체의 존재를 따로 남겨야 후속 운영이 정확해진다.

2026-10-27 보완 · 같은 주소에서 같은 문서를 읽었는가

후속 구현을 읽으며 이 글에 한 가지 조건을 더 남긴다. 주소와 제목, 본문이 같아도 지금 읽는 대상이 원래 입력하던 문서라는 보장은 없다. 남아 있던 내부 복구 함수는 이런 조건만으로 교체된 문서를 인정하고 과거의 실패 단계를 검증 완료로 덮을 수 있었다. 이 문제를 후속 코드와 검증 보고서가 별도 수정 대상으로 기록한다.

바뀐 소스는 실제 호스트가 실패 입력을 기록할 때 실행 모듈의 수명 표시와 프로필·프로젝트·프레임·문서 epoch를 함께 연결한다. 예전 기록에 연결 근거가 없거나 다른 실행 모듈에서 온 기록이면 읽기 확인으로 승격하지 않는다. 같은 살아 있는 문서의 문맥 안에서만 알려진 값의 읽기 대조를 진행하는 좁은 조건이다.

이 수명 표시는 로그인 토큰도, 사이트가 보증하는 영구 문서 ID도 아니다. 앱을 다시 시작한 뒤 같은 주소를 열었다는 이유로 과거 원고가 자동 복원됐다고 설명할 수 없다. 한 페이지 안에서 논리 문서가 바뀌는 모든 경우를 해결한 것도 아니다. 이번 보완에서는 같은 실행 구간의 읽기 확인과 재시작 뒤 문서 복원을 구분한다.

새 읽기 기록은 원래 실패를 가리키되 바꾸지 않는다

현재 소스는 알려진 제목과 대상 필드를 읽기로 대조하며, 대상이 하나인지와 checked·matches가 정확히 참인지 확인한다. 읽기를 기다리는 사이 문서가 바뀔 수 있으므로 비동기 호출 전후의 문맥도 다시 본다. 아직 네이티브 입력이 정리되지 않은 상태에서는 진행하지 않는다. 요청이 돌아왔다는 신호만으로 읽기 증거를 인정하지 않는 조건이다.

확인 뒤에는 현재 작업에 새로운 읽기 체크포인트를 만들고 원래 작업·단계의 식별자를 연결한다. 과거의 실패 단계를 verified로 다시 쓰는 것과는 다르다. 현재 보호 상태를 해제하는 판단과 과거 시도의 종료 상태도 같은 값으로 합치지 않는다. 복구 중 생긴 불완전 절차를 곧바로 재사용 가능한 성공 절차로 등록하지 않는 제한도 남아 있다.

이 연결을 데이터베이스에 영구 보관하거나 실제 페이지의 읽기를 인증하는 일을 아래 예제가 대신하지는 않는다. 코드는 합성으로 선언한 앞뒤 문맥과 읽기 결과를 받아 별도의 메모리 기록을 만든다. 원래 실패와 전달 횟수를 그대로 돌려주고, 실제 보호 해제·읽기·전송·영속 저장은 했다고 표시하지 않는다.

코드와 구현 살펴보기

실제 합성 실행 결과 · 원래 상태는 유지

{
  "original": {
    "attempt_id": "attempt-one",
    "status": "needs_review",
    "send_count": 1
  },
  "new_checkpoint": {
    "id": "read-one",
    "original_attempt_id": "attempt-one",
    "kind": "read_only_evidence",
    "status": "declared_match"
  },
  "reason": "declared_read_matches",
  "performs_read_or_send": false,
  "releases_runtime_guard": false,
  "authenticates_context": false,
  "persistent_receipt_written": false
}

현재 호출 경로와 남아 있는 내부 코드를 나눠 읽었다

후속 보고서는 이 복구 결함의 도달 범위도 구분한다. 현재 부트스트랩의 내장 모델 실행 경로는 제거 상태이고, 기존 UI 재개 요청은 외부 도구에서 현재 결과를 확인하도록 거절한다. 따라서 남은 내부 복구 함수의 재현을 지금의 공개 도구가 일반적인 자동 재개를 제공한다는 증거로 쓰지 않았다.

같은 보고서에는 별도의 공개 텍스트 읽기 경로에서, 부분 입력 뒤 원래 값이 맞는지 확인하는 소유 시험이 남아 있다. 시험에서 외부 fixture가 값을 맞춘 뒤 읽기 증명을 얻었다는 것은 실제 서비스의 원고를 자동 수리했다는 뜻이 아니다. 내부 컴포넌트의 두 프로세스 시험과 공개 읽기 경로의 실행, 기존 원고 보존은 각각의 근거다.

이번 콘텐츠 작업에서는 그 코드·시험 소스·총괄 보고서를 읽었다. 원래 프로그램이나 독립 검수 모델, 브라우저, 계정과 원고, 보호된 실행 영수증을 다시 열거나 시험하지 않았다. 후속 보고서의 검사 수를 이번 합성 예제의 실행 결과에 합산하지 않는다. 현재 지원 범위를 좁혀 쓰는 것이 과거 기록을 부정하는 것은 아니다. 시점과 도달 경로를 더 정확하게 붙이는 일이다.

보완 예제 · 읽기 선언이 원래 기록을 덮지 않게 하기

기존 실패 실험실의 공개 ZIP 4,684바이트와 SHA를 대조하고 추출해 28개 시험을 실행했다. 추가한 읽기 기록 예제는 별도의 22개 시험을 통과했다. 모듈 수명·프로필·프로젝트·프레임·문서 epoch의 차이, 읽기 전후 변경, 오래된 기록, 모호한 대상과 불완전한 참값을 확인한다. 기존 실험실의 실패 영수증과 전달 횟수가 보존되는지도 대조했다.

아래 전체 코드는 새로운 독립 합성 예제다. 같은 실행 문맥의 선언이 맞으면 원래 시도를 참조하는 별도 체크포인트를 돌려주고, 문맥이 없거나 달라지면 새 증거를 만들지 않는다. 입력된 문자열과 참거짓을 비교할 뿐 실제 출처를 인증하지 않는다. 이 코드가 사이트의 안정 문서 ID나 제품의 보안·복구 구현을 대체하지 않는다.

다섯 실행 기록에는 같은 실행 문맥, 예전 연결 없는 기록, 다른 실행 구간, 읽기 뒤 문서 변경과 입증되지 않은 읽기가 있다. 아래에는 같은 실행 문맥의 합성 출력을 실었다. 새 helper는 이 본문에 추가한 것이며 기존 ZIP에 포함되지 않는다. 기존 실험실·원래 실패 기록·기존 공개 주소는 그대로 남겼다.

코드와 구현 살펴보기

독립 합성 전체 구현 · 실제 읽기나 저장 아님

"""Synthetic declared-context ledger. No site read, authentication or guard release."""
from copy import deepcopy
import re


def read_checkpoint(original, stored, before, after, proof, read_id):
    def token(value):
        if type(value) is not str or not re.fullmatch(r'[a-z][a-z0-9_-]{0,39}', value):
            raise ValueError('OPAQUE_ID')
    def binding(value):
        if type(value) is not dict or set(value) != {'runtime', 'profile', 'project', 'frame', 'epoch'}:
            raise ValueError('EXACT_BINDING_FIELDS')
        for key in ['runtime', 'profile', 'project', 'frame']:
            token(value[key])
        if type(value['epoch']) is not int or value['epoch'] < 0:
            raise ValueError('DOCUMENT_EPOCH')
    if type(original) is not dict or set(original) != {'attempt_id', 'status', 'send_count'}:
        raise ValueError('EXACT_ORIGINAL_FIELDS')
    token(original['attempt_id']); token(read_id)
    if read_id == original['attempt_id']:
        raise ValueError('READ_NEEDS_NEW_ID')
    if original['status'] not in ('failed', 'needs_review', 'cancelled'):
        raise ValueError('EXPECTED_UNSETTLED_ATTEMPT')
    if type(original['send_count']) is not int or original['send_count'] not in (0, 1):
        raise ValueError('BOUNDED_SEND_COUNT')
    binding(before); binding(after)
    if stored is not None:
        binding(stored)
    if type(proof) is not dict or set(proof) != {'checked', 'matches', 'target_count', 'native_settled'}:
        raise ValueError('EXACT_PROOF_FIELDS')
    if any(type(proof[k]) is not bool for k in ['checked', 'matches', 'native_settled']):
        raise ValueError('BOOLEAN_PROOF')
    if type(proof['target_count']) is not int or not 0 <= proof['target_count'] <= 64:
        raise ValueError('TARGET_COUNT')

    reason = ('legacy_binding_missing' if stored is None else
              'before_binding_changed' if before != stored else
              'after_binding_changed' if after != stored else
              'native_input_unsettled' if not proof['native_settled'] else
              'target_not_unique' if proof['target_count'] != 1 else
              'read_not_proven' if not (proof['checked'] and proof['matches']) else
              'declared_read_matches')
    checkpoint = ({'id': read_id, 'original_attempt_id': original['attempt_id'],
                   'kind': 'read_only_evidence', 'status': 'declared_match'}
                  if reason == 'declared_read_matches' else None)
    return deepcopy({'original': original, 'new_checkpoint': checkpoint, 'reason': reason,
                     'performs_read_or_send': False, 'releases_runtime_guard': False,
                     'authenticates_context': False, 'persistent_receipt_written': False})

장부는 최종 결론보다 연결을 보존한다

나는 제작 기록을 처음 실패, 원인 가설, 실제 수정, 독립 시험, 후속 관찰과 남은 범위로 연결한다. 이전 시도의 원고나 파일을 공개할 필요는 없다. 식별 자료를 빼고도 어떤 증거가 어느 시점의 주장을 바꿨는지 설명할 수 있다. 동일한 입력의 시도인지 다른 자료를 사용한 후속 작업인지도 구분한다. 그 관계가 없으면 별개의 성공을 원래 실패의 복구 근거로 잘못 붙일 수 있다.

이번 글은 그 기록의 계보를 검토한 것이며 원래 needs_review를 수정하거나 새 생성 요청을 보내지 않았다. 합성 실패 실험실도 원래 운영 기록을 바꾸는 도구가 아니다. 결과가 좋아졌다는 문장 뒤에 무엇이 실패했고 무엇을 다시 하지 않았는지까지 남길 때 시행착오가 다음 제작의 노하우가 된다. 마지막 성공 한 줄로는 얻을 수 없는 재료다.

기존 실패 실험실 ZIP · 새 helper는 본문 참조
기존 공개본 4,684바이트 · read_checkpoint.py 미포함