구현·문서 작업 중 겪은 문제와 그 해결을 재현 가능한 형태로 남깁니다.
이 폴더는 문제 해결 과정과 구현 이력을 기록합니다. 해결·완료된 항목도 삭제하지 않고 상태 표시만 갱신합니다. 회고와 복기에서 "무엇을 어떻게 해결했는가"를 추적하기 위함입니다.
- 해결됨 → 문서 유지 +
상태: 해결됨+ 해결 경로·링크 추가 - 무효화 → 문서 유지 +
상태: 무효(사유)표기 - 삭제 → 하지 않음. 잘못 작성된 문서도 정정으로 처리
※ spec/은 현재 유효한 명세이므로 이 원칙의 대상이 아닙니다(낡은 내용은 갱신·삭제).
| 문서 | 내용 |
|---|---|
| mermaid-headless-validation.md | Mermaid 다이어그램 브라우저리스 문법 검증 (T7·T8) |
| 2026-07-23-fastapi-local-verification.md | FastAPI 로컬 검증 중 런타임/드라이버 이슈 (T16~T18) |
| 2026-07-24-e3-ci-and-search-path.md | E3 CI·런타임 이슈 — lock 플랫폼 종속·pytest pythonpath·search_path (T19~T21) |
| 2026-07-27-e2e-env-issues.md | E2E 검증 환경 이슈 — .env CRLF·register_vector 미등록·한글 인코딩 (T22~T24) |
| 2026-07-28-shared-worktree-and-env-cache.md | 멀티세션 워킹트리 오염 · import 시점 .env 캐시 (T25·T26) |
| 2026-07-30-seeding-quota-and-encoding.md | AI API 판정 쿼터·콘솔 인코딩으로 인한 시딩 중단 (T27·T28) |
| 2026-07-31-local-e2e-and-ci-pitfalls.md | 로컬 E2E·CI 함정 — venv·로그 버퍼·jar 낙후·포트·로그인 쿠키 (T29~T36) |
| 2026-07-31-tau-measurement.md | 후보 임계값 τ 측정 — 틀린 진단·인코딩 재발·대조군 부재 (T37~T39) |
| 2026-07-31-search-cut-measurement.md | 검색 결과 컷 측정 — 반대 방향 질의 부재·worktree .env·배치 구성과 임베딩 재현성 (T40~T42) |
| 2026-07-31-error-contract-pitfalls.md | 오류 응답 계약 검증 — ASGITransport 예외 전파·AI API 스텁 URL 형식·시연 DB 자격증명·색인 갱신 중복 (T50~T52 · T56) |
| 2026-07-31-judge-prompt-ab.md | 판정 프롬프트 A/B — 죽은 설정 키·라벨 커버리지·조건 노출·사전 기준·1회 분포·번호 충돌 (T43~T49) |
| 2026-07-31-db-error-pitfalls.md | DB 오류 분류 — 접속 실패는 asyncpg 예외가 아니다·min_size=1 재현 불가·curl 한글 본문 (T53~T55) |
| 2026-07-31-log-redaction-pitfalls.md | 로그 마스킹 — 잘려서 에코되는 요청 값·거대 본문의 오해를 부르는 400·docstring을 잡는 소스 규약 검사 (T61~T63) |
| 2026-07-31-judge-vote.md | 판정 n회 다수결 — 사라진 선행 산출물·실패와 빈 선택의 혼동·전수 검정·번호 확정 시점 (T57~T60) |
| 2026-07-31-docs-index-check.md | 문서 색인 검사 — union 이 남긴 빈 줄이 표를 끊는다·「색인에 있다」의 범위 (T64·T65) |
| 2026-08-03-dead-config-key-audit.md | 죽은 설정 키 전수조사 — alias 매칭은 grep 으로 못 잡는다·「N 대 M」차집합은 한 방향이 아닐 수 있다 (T66·T67) |
| 2026-08-03-preset-description.md | 프리셋 개정 측정 — 임베딩이 결정적이라는 전제·rank 로 밀려난 손실이 τ 격자에서 사라진다 (T68·T69) |
| 2026-08-03-repeat-incidents.md | 하루에 갈래마다 서너 번 — 사본에서 꺼낸 값·반만 덮은 자동화·공개 저장소 실데이터 (T70~T72) |
| 2026-08-05-multi-signal-investigation.md | 다신호 검색 조사·실측 — 가중합의 정답·무관 동반 상승, Preset 출력 제한과 약어, 키워드 신호 적용 범위, 하네스 버그 2건, 사전 병합 코드의 배포 경로 (T73~T78 잠정) |
| T | 증상 | 해결 |
|---|---|---|
| T1 | main에 직접 커밋됨 | 백업→revert→피처 브랜치 재적용, Conventional Commits 채택 |
| T2 | draft/06·07·09 rebase 충돌(MINYONG 독립 반영) | Option B rebase, 비-AI 개선 보존 + AI 스키마 교체, force-with-lease |
| T3 | PR3 "가드 수정경로 적용 금지" ↔ PR1 모순 | insert-first면 가드 자연통과 → "그대로 통과·특례 금지"로 통일 |
| T4 | updated_at 확정/미결 엇갈림 |
MINYONG 안 채택으로 확정 통일(제거) |
| T5 | gh: command not found(bash) |
C:\Program Files\GitHub CLI\gh.exe 전체경로 호출 |
| T6 | ai 레포 기본 브랜치가 피처 브랜치 | gh repo edit로 main 변경 |
| T7 | Mermaid 검증 실패(@mermaid-js/parser는 flowchart 미지원, jsdom navigator getter-only) | mermaid@11+jsdom, Object.defineProperty로 navigator 우회 → 4/4 valid |
| T8 | Mermaid 추출 파싱 오류(CRLF) | 정규식 .replace(/\r\n/g,"\n") |
| T11 | 마이그레이션 실검증 필요 | pgvector 컨테이너에서 V1→V102 순차·PK·extension·중복스키마 실패 확인 |
| T12 | preset YAML 규칙 위반 방지 | Python 스크립트로 개수/배분/유일/visibility/examples 검증 |
| T13 | docs 11_개발_컨벤션.md 삭제 부작용(pgvector 검토 유일 출처) |
소실 내용 식별·고지 |
| T14 | docs/ai-architecture-diagrams 브랜치가 eval 하네스 커밋 위에 오정렬 |
main 기준 재정렬(rebase --onto) |
| T15 | back ADR "소유 파트: AI 파트" 혼란(표준 ADR엔 소유파트 필드 없음) | 주도(Driver)로 격하 + 레포 스코프 명시(P36) |
| T16 | .env UTF-8 BOM으로 첫 키(DATABASE_URL) 파싱 실패 |
BOM 없이 기록(UTF8Encoding($false)), 첫 3바이트 확인 |
| T17 | pgvector가 VECTOR 컬럼을 Vector 객체로 반환 → np.asarray TypeError |
to_numpy()/to_list() 변환(디코드 방향만) |
| T18 | asyncpg now() - $2 interval 타입 추론 실패(timestamptz < interval) |
$2::interval 명시 캐스트 |
| T19 | Windows uv pip compile lock이 마커 없이 pywin32 고정 → ubuntu CI 설치 실패 |
uv pip compile --universal(sys_platform 마커) |
| T20 | CI 러너에서 pytest가 app/tests import 실패(로컬은 PYTHONPATH 우회) |
pyproject [tool.pytest.ini_options] pythonpath=["."] |
| T21 | search_path=ai 단독이 public 제외 → VECTOR 타입·register_vector가 멀티 커넥션에서 실패 |
ai, public으로 확장(public=vector 확장 소재, core는 경로 밖 유지) |
| T22 | .env가 CRLF라 셸로 값을 뽑으면 \r이 섞여 JSON 파싱 실패(일부 요청만 깨져 혼동) |
tr -d '\r\n' 또는 셸 파싱 금지·get_settings() 사용 (T16 계열) |
| T23 | raw asyncpg 연결은 register_vector 미등록 → VECTOR가 문자열로 디코딩되어 PresetCache 적재 실패 |
스크립트도 app.core.db.Database 사용(search_path+타입 등록 캡슐화). 시딩 재발 주의 |
| T24 | Git Bash + curl에서 한글 본문 인코딩 깨짐(ASCII 본문은 통과 → T22와 증상 동일) |
한글 요청은 Python httpx로 전송, curl은 ASCII 경로에만 |
| T25 | 멀티세션이 단일 git 워킹트리·인덱스·HEAD 공유 → 3파일 커밋에 타 세션 15파일 섞여 push | 격리 git worktree 기본, git add 개별(-A 금지)·커밋 전 브랜치 확인 |
| T26 | main.py 모듈 레벨 create_app() import 시점 .env 캐시 → API 3건만 401(로컬 .env 우연 일치로 은폐) |
settings fixture에서 get_settings() 캐시 재설정 + conftest placeholder env 선주입 |
| T27 | AI API 판정 쿼터는 상수가 아니다 — 공용 게이트웨이라 시점·프로바이더 경로별로 다르다. 07-29 분당 2건 → 07-30 분당 30건 이상 | --pace 기본값 1. 방어는 retry.py 백오프 + 회수 루프. 근본 대책은 벤더 폴백(-175) |
| T28 | 콘솔이 cp949면 — 한 글자에 UnicodeEncodeError → --reset 직후 죽어 데이터만 지워진 상태가 됨 |
sys.stdout.reconfigure(utf-8) + log() 최후 방어. 호출자가 PYTHONIOENCODING을 기억하지 않게 (T22·T24 계열) |
| T29 | python -m uvicorn 이 시스템 Python 을 타서 No module named uvicorn — exit 0 이라 「완료」로 보인다 |
.venv/Scripts/python.exe -m uvicorn |
| T30 | 백그라운드 파이프(| tail)가 서버 로그를 버퍼에 가둔다 — 살아 있는 동안 0바이트 |
파이프 대신 > file 2>&1 |
| T31 | gms window 가 안 나오는 것을 계측 실패로 오판 — 창은 타이머가 아니라 다음 호출이 닫는다 |
60초 뒤 1건 더 넣으면 나온다 |
| T32 | back jar 이 낡으면 Flyway 가 Detected applied migration not resolved locally: 6 으로 기동 거부 |
git pull 뒤 ./gradlew bootJar |
| T33 | ai/.env 의 DATABASE_URL 이 07-27 잔재 :5433 — 시연 정본은 :15432 |
preflight 가 BLOCK 한다. 기본값은 그대로 |
| T34 | 브라우저 로컬 테스트 로그인은 logged_in=1 정확 일치 — true 는 안 걸린다 |
access_token + logged_in=1 + XSRF-TOKEN 셋을 심는다 |
| T35 | 검색이 소유자별로 갈려 계정을 잘못 잡으면 데이터가 없는 것처럼 보인다 | social_account 로 member 별 Context 수를 먼저 센다 |
| T36 | CI 검사를 dev 에만 넣으면 main 기반 PR 에 적용되지 않는다(PR CI 는 head 워크플로로 돈다) |
hotfix/* 로 main 에도 올린다 |
| T37 | 「후보 임계값이 없어서 오분류가 난다」는 진단이 틀렸다 — _topk 에 이미 있었고 테스트도 둘 있었다. 후보도 10개가 아니라 평균 7.4개 |
「기능이 없어서」는 grep 한 줄로 확인하고 쓴다. 실제 원인은 적합·부적합 유사도 분포의 겹침 |
| T38 | 스크립트에만 T28 방어를 넣으면 탐색용 python -c 한 줄에서 다시 죽는다 — 앞줄은 이미 찍혀서 완료로 보인다 |
한 줄에는 PYTHONIOENCODING=utf-8, 반복할 것이면 파일로 옮긴다 |
| T39 | 재판정 차이를 대조군 없이 읽으면 전부 조건 탓이 된다 — 같은 τ 로 다시 판정만 해도 Context 26% 가 흔들린다 | 판정(LLM) 계층 측정은 대조군을 같이 돌린다. 임베딩은 결정적이라 불필요 |
| T40 | 정답이 있는 질의만 재면 컷의 절반이 안 보인다 — r 이 무관 질의를 15건 중 0건도 침묵시키지 못한다는 사실이 검증 질의 12건에서는 드러나지 않는다 |
걸러져야 하는 입력을 표본에 넣는다. 「0건」의 부호가 축마다 반대이므로 표를 가른다 |
| T41 | worktree 에 .env 가 없어 get_settings() 가 GMS_API_KEY 부터 죽는다(env_file 은 CWD 기준·gitignore) |
.env 를 worktree 에 복사하고 DATABASE_URL 만 환경변수로 덮는다. .demo/ 키 분기(-198)와 같은 원인 |
| T42 | 임베딩 배치 구성이 바뀌면 같은 텍스트의 유사도가 10⁻⁴ 규모로 흔들린다(0.5264→0.5258). 「임베딩은 결정적」은 같은 배치일 때의 이야기다 |
그 규모 차이가 결론을 가르는 값을 채택하지 않는다. 재현용으로 유사도 행렬을 커밋한다 |
| T50 | httpx.ASGITransport 는 앱 예외를 응답으로 바꾸지 않는다(raise_app_exceptions=True 기본) — 「500 이 나간다」를 단언하려는데 예외가 테스트로 튄다 |
500 을 보려면 raise_app_exceptions=False. 무엇이 새는지 보려면 기본값 그대로 |
| T51 | 로컬 AI API 스텁 URL 에 /gmsapi/ 가 없으면 앱이 기동에서 죽는다(_gms_base_url_shape) — 증상이 「스텁이 안 불린다」가 아니라 「안 뜬다」다 |
스텁도 /gmsapi/api.openai.com/v1/embeddings 경로를 흉내낸다 |
| T52 | 시연 DB 는 포트뿐 아니라 비밀번호도 .env 와 다르다(pinlog-local). T33 대로 :15432 만 고치면 InvalidPasswordError |
DSN 일부만 고치지 않는다. docker inspect 로 컨테이너 env 를 직접 읽는다 |
| T43 | .env 의 PINLOG_JUDGE_MODEL 은 -175 가 대체해 읽히지 않는데 값이 그럴듯해(체인 2순위) 리포트의 판정 모델을 잘못 적게 한다. 실제 응답은 체인 1순위 gpt-4o-mini |
측정 도구는 벤더를 인자로 받고, 읽은 값이 아니라 답한 값(JudgeResult.model)을 남긴다 |
| T44 | labels.yaml 은 현행 판정 83행만 덮는다 — τ 스윕과 달리 재판정은 없던 행을 만들어 표 밖으로 나간다(24종). 빼고 세면 조건 비교가 기운다 |
원본은 고치지 않고 labels_extra.yaml 로 넓힌다. 남는 것은 unlabeled 로 세어 양극단으로 돌린다 |
| T45 | 라벨을 붙일 때 어느 조건이 고른 행인지 보이면 라벨이 결론을 따라간다 — 증상이 없고 개선 폭만 부푼다 | 덤프에서 조건·회차를 뺀다. 본문과 description 만 보고 붙인다 |
| T46 | 사전 기준(범위 비중첩)을 못 넘으면 자를 바꾸고 싶어진다. 표본이 늘면 그 기준은 오히려 통과하기 어려워져 교체가 정당한데, 교체 시점이 결과를 본 뒤다 | 표본을 늘리고 자를 더한다(순열검정). 앞의 자를 지우지 않고 둘 다 찍는다 |
| T47 | DB 에 저장된 판정 1회분의 confidence 가 라벨을 깨끗이 가르는 것처럼 보인다(fit min 0.70 · unfit 0.30~). 한 번 더 판정받으면 분리가 사라진다 — confidence 도 비결정적이다. DB 조회가 공짜라 여러 번 볼 생각을 안 하게 된다 |
저장된 판정에서 발견한 문턱은 후속으로 올리기 전에 같은 조건으로 한 번 더 받아 대조한다(42회·70초). 확인용 회차는 --outdir 를 나눈다 |
| T48 | 트러블슈팅 번호를 분기 시점의 마지막 다음으로 매기면, 같은 날 병렬로 병합된 티켓과 겹친다(-213 이 T40~T42 를 먼저 썼다). 색인 표는 정렬되지 않아 중복이 나란히 서지 않는다 |
PR 직전 origin/dev 로 rebase 한 뒤에 번호를 확정한다. 재번호는 매핑 한 번으로(역순 치환은 뒤섞인다), 남의 행은 건드리지 않는다 |
| T49 | open(p,'w').write(f(s)) 에서 f 가 죽으면 파일이 0바이트로 남는다 — 'w' 가 인자 평가 전에 truncate 한다. 고쳐서 재실행하면 빈 파일을 읽어 빈 결과를 써서 조용히 성공한다 |
변환 결과를 먼저 만들고 임시 파일에 쓴 뒤 os.replace. newline='' 를 양쪽에. 문서 일괄 수정은 커밋 뒤에 돌린다 |
| T53 | DB 접속 실패는 asyncpg 예외가 아니다 — 포트 거부·DNS·타임아웃·풀 획득 타임아웃이 전부 stdlib OSError 다. 08xxx 는 붙어 있던 연결이 끊길 때의 SQLSTATE 다. asyncpg 만 분류하면 테스트는 전부 초록인데 「DB 연결 실패가 503」만 거짓으로 남는다 |
접속 단계에서만 OSError 를 함께 번역하고, 분류를 repository 함수가 아니라 세션 경계에 건다(pool.acquire() 가 함수 밖이다) |
| T54 | 닿지 않는 주소로 「요청 중 DB 불가」를 재현하려 하면 create_pool 이 min_size 만큼 즉시 접속해 픽스처에서 죽는다. connect() 를 건너뛰면 RuntimeError(우리 결함·500)라 다른 것을 측정한다 |
테스트용 min_size=0 풀로 접속 시점을 첫 acquire() 로 미룬다. 풀·드라이버·예외는 실물로 두고 예외를 주입하지 않는다 |
| T55 | Git Bash curl -d 로 한글 본문을 보내면 {"detail":"There was an error parsing the body"} 400 — 스키마 결함처럼 보인다 |
질의어만 ASCII 로 바꿔 같은 요청이 통과하면 원인은 본문 바이트다. 한글이 측정 대상이면 UTF-8 파일 + --data-binary @file |
| T56 | merge=union 은 색인 행을 갱신하면 낡은 판을 되살려 같은 문서가 두 줄이 된다 — 추가만 할 때는 안 난다 |
행을 고쳤으면 병합 뒤 uniq -d 로 확인한다 |
| T57 | 후속에 「하네스에 기록을 넣어 두었다」로 물려주면 코드에 기능이 있다는 뜻인데 읽는 쪽은 데이터가 남아 있다로 읽는다. .prompt_ab/ 는 .gitignore 에 이름조차 없어 수명을 정한 사람이 없었고 worktree 와 함께 사라졌다 |
산출물은 경로와 수명을 함께 물려준다. 안 남길 파일도 .gitignore 에 이름과 이유를 적는다 |
| T58 | 다수결은 「고르지 않았다」를 정보로 쓰므로 「고르지 않았다」와 「묻지 못했다」가 같은 모양이면 호출이 실패할수록 오분류가 줄어 보인다. 판정 1회 집계에서는 둘을 가를 이유가 없어 드러나지 않는다 | 분모를 성공 수가 아니라 n 으로 고정하고 정족수 미달이면 저장하지 않는다(PROCESSING 유지). 서비스와 측정이 같은 함수를 부른다 |
| T59 | score_ab.py 의 순열검정은 근사가 아니라 전수라 조건 간 관측 수가 어긋나면 폭발한다(30 대 10 → C(40,10) ≈ 8.5억). 접어서 만드는 측정은 조건마다 관측 수가 자연히 달라진다 |
기술 수치는 관측 전부로 내고 검정을 걸 때만 --cap 으로 수를 맞춘다. 자를 무르는 것과 표본을 맞추는 것은 다르다 |
| T60 | 색인이 낡아 번호를 틀리게 하고(본문 T50-221 이 T53~T56·I32 를 먼저 가져갔다 |
T48 의 처방이 유일하게 듣는다 — 병합 뒤에 번호를 확정한다. 문서를 쓰는 동안은 번호를 미루고 매핑 한 번으로 치환. T 와 I 를 함께 본다 |
| T64 | merge=union 이 표 안에 남긴 빈 줄이 GFM 표를 끊는다 — 그 뒤 행이 파이프 문자 문단으로 렌더된다. 에디터·diff 에서는 표로 보여 아무 증상이 없다. union 은 「추가만 하면 안전」으로 통하지만(T56 은 갱신을 겨눴다) 추가하는 줄에 빈 줄이 딸리면 추가만으로 깨진다 |
새 행은 빈 줄 없이 마지막 행 바로 아래 붙인다. 색인을 고쳤으면 GitHub 의 렌더된 화면을 한 번 본다 — 색인은 고치려고 여는 파일이라 읽는 사람이 없다 |
| T65 | 「색인에 있는가」를 README 전체 링크로 재면 전수 표(I##)의 링크가 파일 표의 누락을 가린다 — 착수 시점 dev 위반 3건 중 2건이 그 상태라 검사가 자기가 겨눈 사고(07-31 사고 3)를 못 잡는다 |
판정 범위를 파일 표 섹션으로 좁힌다. 어느 표가 색인인지를 먼저 정하지 않으면 검사는 가장 느슨한 해석을 택하고, 느슨한 해석은 원래 잡으려던 것을 통과시킨다 |
| T61 | 오류 본문이 요청 값을 에코하는지 완전 일치로 재면 「안 샌다」가 나온다 — OpenAI 는 앞뒤 3자만 남기고 잘라서 되돌린다(Invalid value: 'PIN...def'). 이 사실이 마스킹 규칙의 근거 전체였다 |
완전 일치로 끝내지 않고 본문 원문을 본다. 자동화하려면 마커의 앞/뒤 n자도 함께 찾는다 |
| T62 | 요청 본문이 아주 크면 AI API 가 Model not found in request for domain ... 400 을 낸다 — model 은 멀쩡히 들어 있다. PermanentError 로 분류돼 재시도 없이 FAILED 가 되는데 문구는 모델명을 가리킨다 |
「모델이 없다」400 은 설정을 고치기 전에 본문 크기부터 본다. 같은 모델명으로 짧은 요청이 통과하면 원인은 모델명이 아니다 |
| T63 | 소스 규약(resp.text 를 마스킹 없이 쓰지 않는다)을 정규식으로 훑으면 그것을 설명하는 docstring 이 위반으로 잡힌다 — 예외 목록을 손으로 유지하면 검사가 규약보다 약해지는 방향으로만 고쳐진다 |
ast.parse 로 본다. 문자열 리터럴은 후보에 들어오지 않으므로 예외 목록이 필요 없다 |
| T66 | pydantic Settings 는 필드명을 소문자로 선언해도 alias= 로 지정한 대문자 env 를 읽는다 — .env·문서에 키 이름이 리터럴로 남아 있어도(전부 "이제 안 읽는다"는 설명 맥락) 그 문자열 존재가 "읽힌다"의 증거가 아니다. -210(threshold 가 config.py:114에 있었던 사고)과 정반대 모양의 오판 |
후보 키마다 sentinel 값을 주입해 실제로 Settings() 를 생성하고 필드 값에 반영되는지 확인한다. os.environ 을 통째로 비우면 Windows asyncio.windows_events 가 SYSTEMROOT 를 못 찾아 죽으므로 필요한 키만 얹었다 뗀다 |
| T67 | ".env 13 대 .env.example 12" 를 단일 방향 차집합(13-12=1)으로 예단하면 틀린다 — 실제로는 .env 만의 키가 2개(둘 다 죽음), .env.example 만의 키가 1개(살아 있음)였다. 공통 11 + 2 = 13, 공통 11 + 1 = 12 로 총계는 맞지만 "하나만 다르다"는 전제와 실제 구성이 다르다 |
두 파일의 키 이름 집합을 대칭차(symmetric difference)로 비교한다 — 숫자 차이만으로 "어느 하나를 채우면 끝"이라 판단하지 않는다 |
| T68 | 임베딩 API 는 같은 배치 구성으로도 결정적이지 않다. 시드 그대로인 조건의 벡터가 DB 적재분과 코사인 0.9993 이라 "경로가 어긋났나"로 읽힌다 — 단건으로 다시 뜨면 1.000000 이 나와 "배치 경로 결함"으로 오진하기 쉽다. T42 가 배치 구성 변경을 원인으로 지목했으나 필요조건이 아니다. 실측 |Δsim| 최대 0.0044 · rank 43쌍 |
같은 배치를 두 번 떠서 서로 대조한다(DB 와만 비교하면 어느 쪽이 흔들리는지 모른다). 검증 문턱은 0.999 — 텍스트가 실제로 다르면 0.9 대로 떨어져 경로 어긋남은 그래도 잡힌다. base2(같은 텍스트) 조건을 상시 두어 흔들림이 후보 절단선을 넘는지 매 측정에서 본다 |
| T69 | tau_grid/score.py 의 τ 격자가 rank > K 로 밀려난 행을 어느 칸에도 세지 않는다 — 같은 파일의 라벨 분포는 fit 최솟값 0.2554 를 찍는데 τ=0.30 격자의 「fit 유실」은 0 으로 나온다. τ 만 조건일 때는 rank 가 고정이라 무해했고, 임베딩이 바뀌자 같은 코드가 조용히 틀린 값을 낸다(교환비가 0.00 으로 보이나 실제 0.67) |
밀려난 행을 따로 세어 나란히 낸다. score.py 는 -210 수치의 재현 기준점이라 고치지 않았다. 고칠 거라면 「rank 로 빠짐」을 τ 유실과 다른 칸으로 센다 — 합치면 「τ 를 올려서 잃었다」와 「다른 프리셋이 밀어내서 잃었다」가 구분되지 않는다 |
| T70 | 낡은 값이 새 결정의 근거가 되고 그 사실이 드러나지 않는다. 하루에 넷 — 철회된 단정이 그 문서를 근거로 인용하며 되살아나고, 코드 한 행을 인용하며 바로 다음 행을 놓친 오독이 이미 병합된 작업의 설계 근거가 되고, 원장에 적어 둔 정정이 계약에서 원래 문구로 되돌아오고, 계약에 실은 수치가 여러 번 틀렸다. 넷 다 사본이 아니라 원문을 연 다른 사람이 잡았다 — 사본을 읽는 쪽은 확인할 것이 있다는 사실 자체를 모르고, 「쓰기 직전 재조회」 규칙은 이미 있었는데도 났다 | 읽는 쪽이 아니라 쓰는 쪽에 규칙을 둔다(R-18 「값 대신 출처」) — 측정 수치·현행 상수·코드 행 번호·배포 상태 값을 옮기지 않고 파일명·모듈명·티켓·PR 번호로 좌표만 가리킨다. 기계 검사는 없다 — 「제안 내용인가 관측값인가」는 사람이 묻는다. 철회하는 쪽도 대상을 좁혀 적는다 — 폐기된 것은 값이 아니라 값의 항상성이다 |
| T71 | 자동화가 success 로 끝나는데 아무것도 안 바뀐다. 병합 직전 재검사가 경합에 지면 PR 이 낡은 SHA 를 든 채 남고 갱신 워크플로가 다시 밀어야만 재시도되는데 그 cron 이 연속으로 빠졌다. 봉인 값은 갱신 도구의 수정 대상 필드 밖이라 틀려도 실패하는 검사가 없고, 감시 스크립트는 세션이 죽어도 살아남아 출력이 아무데도 안 닿는 고아가 된다. 개수로 세는 판정은 원리상 부정확해 이것을 오래 숨긴다 |
성공 판정을 네 층으로 가른다 — 프로세스 생존 ≠ 실행 성공 ≠ 실제 변경 발생 ≠ 최종 전달 성공. 감시는 heartbeat 로 자기 상태를 파일에 남기고 확인 경로가 나이·출처·중복을 보고하되 판정은 사람이 한다. 배포 자동화 둘은 인프라 소관이라 이슈로 기록만 했다. 「다음 스케줄이 복구한다」는 전제 자체가 약하다 |
| T72 | 방어를 세운 뒤에도 같은 종류의 노출이 새로 생긴다. 대응을 정한 직후 측정 산출물이 새로 커밋됐다 — 훅은 바깥으로 나가는 발화(이슈·PR·코멘트·티켓)만 검사하는데 그 산출물은 스크립트가 만들고 사람이 git add 해 검사 지점을 한 번도 안 거쳤다. 커밋 시점 검사가 없고, .gitignore 예외가 「커밋하라」고 적극적으로 지시하고 있었다. 마스킹한 이슈의 코멘트가 가리지 않은 쪽을 안내하고 있었다 |
미정. 선택지 넷 — 산출물 마스킹본 교체(그것을 읽는 재판정 도구가 깨질 수 있다) · 산출물 제외(재측정 비용 감수) · 검사 범위를 커밋 시점으로 이동 · 마스킹 문구의 경로 안내 제거(이것만 즉시 가능). 재측정 비용과 노출이 정면으로 부딪히는 구조라 「지우면 끝」이 아니다. 위치·규모는 문서에 적지 않는다(위치 비공개 선례) |
| T73 | (잠정) 키워드 점수를 가중합으로 더하자 컷 기준 아래의 정답이 결과에 복구됐지만, 같은 계산으로 관련 없는 후보도 기준을 넘어 노출됐다 — 관련 없는 문장형 질의의 무노출이 11/15 에서 9/15 로 감소. RRF 는 무노출을 유지했지만 문장형 hit@1 이 0.8333→0.5833 으로 퇴행. 검색 방식 수가 아니라 병합 규칙이 결과를 정한다 | floor 를 sweep 필수 축으로. floor 0.35 · binary weight 0.05 조합만 채택 기준 4종 통과 — 대가는 키워드 적용 범위 축소와 신한 재제외. 검색 방식별 컷·게이트 분리(P49 §5) |
| T74 | (잠정) P48 2단계가 LLM 출력을 Preset 목록으로 제한해, Preset 에 없는 부트캠프로 부캠을 확장할 수 없다 — 2단계의 목적인 약어 문제를 그 제한이 막는다. 문자열(본문에 「부캠」 없음)·임베딩(의미 연결 실패, 컷 전 8위)·Preset(표현 항목 없음) 모두 이 질의를 해결하지 못해, 검토한 대안 중 LLM 재작성이 가장 현실적 |
출력 제한을 풀고 자유 재작성으로 개정 제안(P49 §3). 잘못된 재작성 방어는 원문 복귀·질의 캐시·보수 프롬프트로. 재작성 목표 문장의 검색 성공은 기측정(부트캠프 1위) |
| T75 | (잠정) 관련 없는 결과가 노출되지 않는 floor(0.35)에서 키워드 점수가 단어형 질의 66건 중 12건에만 반영된다(미반영 54건). 키워드 적용 범위와 무관 결과 차단이 floor 값 하나로 맞바뀐다 | 미반영 54건 목록을 Preset 개선의 설계 입력으로. 키워드 채택값 확정만 Preset 개정에 종속 — 인수·재작성·문자열 검색은 무관(P49 §8) |
| T76 | (잠정) keyword_matrix.py _parse_vector 가 pgvector 코덱 등록 커넥션이 반환하는 Vector 객체를 처리하지 못해 TypeError 로 실행 중단 — 코덱 미등록 환경(문자열 반환)에서는 재현되지 않을 수 있다. T17 과 같은 반환형 차이 |
문자열·Vector·iterable 세 반환형을 모두 받는 분기(to_list). 실측은 로컬 패치 3줄로 완료 |
| T77 | (잠정) rank_score.py·fusion_sweep.py 에 stdout UTF-8 재설정이 없어 cp949 콘솔에서 — 출력 시 인코딩 예외로 중단 — 같은 브랜치 keyword_matrix.py 에는 재설정이 있다. 한 파일만 방어된 T28·T38 과 같은 유형 |
재설정 3줄을 두 파일에 복제. 실측은 PYTHONIOENCODING=utf-8 로 우회 |
| T78 | (잠정) dev 에 미리 병합한 코드가 다른 작업의 main 반영에 포함되어 배포될 수 있다 — -266 코드가 별도 릴리스 판단 없이 main 에 반영되고 dev 환경까지 배포된 선례. 기본 off 플래그 방식은 dev 에 코드가 존재하는 한 의도치 않은 배포 경로가 남아 기각 |
통합 브랜치(search-upgrade) 격리 + dev→통합 단방향 동기화 + 검증 기준 통과 후 한 번만 병합(P49 §6). 플래그는 운영 중 복귀 장치로만 |
T9(H2·pgvector)·T10(flyway.schemas)은 백엔드 아티팩트라 back 레포
docs/ai/troubleshooting에 있습니다.