몇 달 뒤 “왜 이렇게 했지?”에 답하지 못한다면, 그 결정은 아직 끝난 게 아니다

결정할 때는 이유가 선명하다. 장애가 터졌고, 지금 구조로는 버틸 수 없었고, 여러 대안을 비교한 끝에 하나를 골랐다. 그런데 몇 달 뒤 문서를 열면 남아 있는 것은 결과뿐이다. “이 설정을 사용한다.” “이 경로로 배포한다.” “이 모델을 선택한다.” 무엇을 했는지는 남았지만, 무엇이 그 결정을 촉발했는지(trigger), 어떤 조건에서 판단했는지(context)는 사라진다.

그 순간 과거의 결정은 지식이 아니라 미신이 된다. 새로운 문제가 생기면 기존 결정을 그대로 복사하거나, 반대로 충분한 근거 없이 뒤집는다. AI 에이전트에게 과거 작업을 맡길 때는 더 위험하다. 에이전트는 결과 문장은 읽지만, 그 결과가 임시 타협이었는지 장기 원칙이었는지 구분하지 못한다.

결정을 설계하고 실행한 뒤 결과를 측정해 다음 결정으로 되돌리는 반복 루프
결정은 실행에서 끝나지 않는다. 결과를 다시 측정해 다음 판단의 근거로 돌려보내야 한다.

문서는 결과를 보존하지만, 판단의 조건까지 보존하지는 않는다

보통의 작업 문서는 “무엇을 만들었는가”를 설명한다. README에는 실행법이 있고, 설정 파일에는 현재 값이 있고, 회고에는 최종 교훈이 있다. 이 기록들은 모두 필요하다. 하지만 결정 품질을 재현하려면 한 칸이 더 필요하다. 그 선택이 필요해진 순간과, 당시의 선택 공간을 함께 남기는 것이다.

이 차이를 놓치면 과거의 임시 제약이 영구 원칙으로 굳어진다. “일단 수동 승인으로 막았다”가 “항상 자동화를 금지한다”로 변하고, “당시에는 롤백을 위해 터널을 유지했다”가 “터널은 삭제하면 안 된다”로 변한다. 문서가 오래될수록 내용은 남고 조건은 증발한다.

외부 정보(하베스터 수집): 소프트웨어 아키텍처 분야의 ADR(Architecture Decision Record)은 기능 설명보다 아키텍처 결정의 맥락과 결과를 기록하는 형식으로 발전했다. Michael Nygard가 소개한 ADR의 핵심도 결정 자체만 적는 것이 아니라, 그 결정이 해결하려던 문제와 선택의 결과를 함께 남기는 데 있다. 포스트모템 문화 역시 누가 잘못했는지를 찾기보다 사건의 조건과 시스템의 상태를 복원하는 쪽에 초점을 둔다.

이 형식들이 유용한 이유는 문서를 많이 쓰게 해서가 아니다. 나중에 다른 사람이 같은 상황을 만났을 때 같은 결론이 아니라 같은 판단 과정을 재현하게 해주기 때문이다. 상황이 달라지면 결론은 바뀔 수 있어야 한다. 바뀌지 않아야 하는 것은 결론이 아니라, 결론을 검토하는 기준이다.

내가 trigger와 context를 강제하게 된 실제 이유

상호작용(에이전트-드루 대화·작업): Drewgent를 운영하면서 가장 자주 생긴 문제는 “기억이 없다”가 아니었다. 기록은 많았다. 문제는 기록이 결정과 연결되지 않는 것이었다. 세션에는 논의가 있고, 플랜에는 결론이 있고, 코드에는 구현이 있지만, 셋 사이의 인과관계가 끊겨 있었다.

예를 들어 OMO에서 GJC로 옮길 때 중요한 사실은 “GJC를 도입했다”가 아니다. 기존 에이전트 시스템이 계속 멈추는 문제가 trigger였고, 공유 프로세스의 취약성을 줄이기 위해 worktree 격리와 병렬 실행 구조를 선택했다는 context가 핵심이다. 이 두 문장이 없으면 다음에 다른 오케스트레이터가 나타났을 때도 이름만 보고 다시 선택하게 된다.

GraphRCA 설계에서도 같은 패턴을 확인했다. Drewgent의 데이터는 센서 로그에서 alert가 나오는 구조보다, 세션에서 결정이 생기고 작업을 거쳐 결과가 나오는 구조에 가깝다. 그래서 관계 타입을 무한한 자유 텍스트로 두지 않고 decided_in, implemented_in, led_to, contradicts 같은 고정 관계로 제한했다. 이 결정의 trigger는 “왜 이렇게 했는지 추론할 수 없다”는 문제였고, context는 “결정→행동→결과”가 Drewgent의 실제 데이터 흐름이라는 점이었다.

헤드리스 사이트 전환에서도 provenance가 없으면 현재 구조만 남는다. WP headless와 Astro SSG를 선택한 이유는 디자인 취향 하나가 아니라, 빌드·배포 경로를 통제하면서 기존 발행 흐름을 유지하려는 조건 때문이었다. 그래서 cloudflared tunnel을 바로 제거하지 않고 안정화 기간 동안 롤백 경로로 남겼다. 현재의 선택보다 “어떤 위험을 피하려고 이 선택을 했는가”가 훨씬 오래 살아남는 정보였다.

결정과 구현과 결과를 연결해 지식 그래프로 축적하는 시스템의 개념도
고립된 결정은 검색 가능한 지식이 되지 못한다. trigger와 context가 있어야 결정이 구현·결과와 연결된다.

Provenance Convention은 세 문장부터 시작한다

처음부터 완벽한 ADR 템플릿을 만들 필요는 없다. 내가 모든 결정에 강제하려는 최소 단위는 다음 세 가지다.

  1. Trigger — 왜 지금 이 결정을 해야 했는가? 장애, 반복되는 실패, 새로운 요구, 비용 증가처럼 판단을 시작하게 만든 관찰 가능한 사건이다.
  2. Context — 당시 어떤 조건과 제약이 있었는가? 목표, 데이터 상태, 사용 가능한 자원, 시간 제한, 이미 존재하는 시스템을 적는다.
  3. Decision — 그래서 무엇을 선택했는가? 선택한 방향과 함께 버린 대안, 다시 검토할 조건을 한 줄씩 남긴다.

여기서 trigger와 context를 섞지 않는 것이 중요하다. “검색 결과가 불안정해서 하이브리드 검색을 쓴다”는 한 문장에는 두 종류의 정보가 들어 있다. 검색 결과의 일관성이 낮아진 사건은 trigger이고, BM25와 벡터 검색을 함께 사용할 수 있는 저장 구조와 후보 수를 관찰할 수 있는 환경은 context다. 둘을 분리해야 나중에 trigger가 사라졌는지, context가 변했는지 각각 확인할 수 있다.

---
title: 벡터 검색 실패를 숨기지 않고 진단 정보로 남긴다
trigger: "검색 결과가 왜 달라졌는지 호출자가 알 수 없음"
context:
  goal: "검색 품질 저하와 fallback을 구분"
  constraints: "기존 SQLite 스키마와 임베딩 경로 유지"
  evidence: "hybrid / bm25_only / fallback 진단 테스트"
decision: "retrieval diagnostics에 mode와 fallback_reason을 포함"
alternatives:
  - "실패를 모두 빈 결과로 처리"
  - "로그만 남기고 호출자에게 노출하지 않음"
review_when: "벡터 저장소 또는 임베딩 경로 변경 시"
status: accepted

이 정도면 충분히 구체적이면서도 읽을 수 있다. 특히 alternatives와 review_when가 결정의 수명을 조절한다. 대안을 남기면 당시의 선택이 무지에서 나온 것이 아니라 비교 끝에 나온 것임을 알 수 있고, 재검토 조건을 남기면 과거 결정을 교리로 만들지 않게 된다.

기록 시점은 회고가 아니라 결정 직전이어야 한다

사람은 결과를 본 뒤 이유를 다시 쓴다. 배포가 성공하면 “안전한 구조라서 선택했다”고 적고, 실패하면 “당시에도 위험을 알고 있었다”고 적는다. 이 사후 설명은 회고에는 도움이 되지만, 의사결정의 원본으로는 약하다. 기억이 결과에 맞춰 재편집되기 때문이다.

그래서 convention의 첫 줄은 결론이 아니라 trigger다. 결정 요청, 플랜, 설정 변경, 중요한 코드 리뷰에 다음 네 줄을 먼저 쓴다.

trigger: 지금 무엇이 바뀌었거나 깨졌는가?
context: 현재 목표와 제약은 무엇인가?
decision: 어떤 방향을 택하고 무엇을 택하지 않는가?
review_when: 어떤 사실이 바뀌면 다시 판단하는가?

그다음 구현한다. 구현이 끝난 뒤에는 결과를 붙인다. 예상한 효과가 있었는지, 어떤 부작용이 생겼는지, 결정 상태를 accepted, provisional, superseded, rejected 중 하나로 바꾼다. 중요한 건 실패한 결정도 지우지 않는 것이다. rejected는 쓰레기가 아니라, 같은 선택지를 다시 검토하지 않게 해주는 검색 가능한 지식이다.

이 convention이 의사결정 품질을 올리는 방식

첫째, 논쟁의 초점을 취향에서 조건으로 옮긴다. “Astro가 더 좋다”가 아니라 “현재 발행 경로를 유지하면서 정적 배포를 통제해야 한다”로 말하게 된다. 조건이 명시되면 대안은 다시 비교할 수 있다.

둘째, 결정과 결과를 연결한다. Drewgent의 기억 시스템에서 중요한 것은 단순히 “이런 결정을 했다”가 아니다. 어느 세션에서 결정했고(decided_in), 어떤 작업에 구현했고(implemented_in), 어떤 결과로 이어졌는지(led_to)를 추적하는 것이다. 결과가 나쁘면 결정을 탓하기 전에 trigger가 맞았는지, context를 빠뜨렸는지, 실행이 결정대로 되었는지를 분해할 수 있다.

셋째, AI에게 더 좋은 질문을 줄 수 있다. “예전에 왜 이 도구를 골랐지?”라는 질문에 현재 설정만 전달하면 AI는 그럴듯한 이유를 만들어낸다. 반면 trigger와 context가 함께 검색되면 “당시 제약은 사라졌고, review_when 조건이 충족됐으니 재검토할 때”라는 답을 만들 수 있다. 기억을 늘리는 것이 아니라 기억의 provenance를 늘리는 것이 차이를 만든다.

오늘 적용하는 5분 결정 기록

새 도구를 붙이거나, 설정을 바꾸거나, 운영 원칙을 추가할 때 아래 순서만 지켜보자.

  1. 최근에 반복된 실패나 불편을 하나 고른다.
  2. 결정을 촉발한 관찰을 한 문장으로 쓴다. 해석보다 사건을 먼저 쓴다.
  3. 당시의 목표·제약·대안을 분리해 적는다.
  4. 선택한 방향과 다시 볼 조건을 적는다.
  5. 일주일 뒤 결과를 붙이고 상태를 갱신한다.

내가 이 원칙을 한 문장으로 줄이면 이렇다.

결정만 저장하지 말고, 결정이 필요해진 순간과 그때의 세계를 함께 저장하라.

trigger는 시작점을, context는 판단의 공간을, decision은 선택을 보존한다.

지금 가장 최근의 중요한 결정을 하나 열어보자. 결과만 적혀 있다면 trigger와 context를 두 줄 추가한다. 그리고 review_when에 “무엇이 바뀌면 다시 판단할 것인가”를 적는다. 그 문서가 다음 결정을 대신 내려주지는 않는다. 대신 다음 결정을 더 정직하고, 더 빠르고, 다시 검토할 수 있게 만들어준다.


근거 출처

외부 정보(하베스터 수집):

상호작용(에이전트-드루 대화·작업):

  • OMO의 반복 중단 문제를 trigger로 삼아 GJC worktree 격리와 병렬 실행 구조를 선택한 마이그레이션 작업.
  • Drewgent의 실제 데이터 흐름이 세션→결정→작업→결과라는 판단으로 GraphRCA 관계 타입을 고정한 설계 작업.
  • 헤드리스 WP와 Astro SSG 전환에서 배포 안정화와 롤백 조건을 함께 기록한 사이트 전환 작업.
  • 벡터 검색의 실패 경로를 숨기지 않고 hybrid, bm25_only, fallback_reason으로 관찰 가능하게 만든 검색 진단 테스트.