v3.6.0 — Phase 1: Provenance (영구기억 출처 추적)
v3.6.0 — Phase 1: Provenance (영구기억 출처 추적)
Summary
"신뢰 가능한 효과적 회수(trustworthy effective recall)"를 향한 Phase-1 기반 로드맵의 첫 축 — 출처 추적(Provenance). 영구기억이 어디서 왔는지(어떤 세션·소스에서, 언제 포착됐는지)를 staging 시점에 frontmatter에 기록하고, 회수될 때 그 출처를 결과에 부착·표시한다. 사용자가 회수된 메모리를 볼 때 "이게 언제·어디서 온 기억인지"를 한 줄로 확인할 수 있어, AI의 "딴소리"를 검증·신뢰하는 근거가 된다.
12 커밋. 기능 골격(write → recall → format)을 먼저 깔고, 다단계 적대적 버그헌팅(find → adversarial verify → fix-the-fix, 2 라운드 + completeness sweep) 으로 프로덕션 갭과 직렬화 취약점을 교차 발견·수정했다. provenance 테스트 +28 (전체 668개 수집 · 667 passed · 1 skipped(pre-existing) · 25 subtests passed).
Added
- staging 시 출처 기록 (
write_staged): 영구기억 후보를 staged할 때source_type/source_ref를 frontmatter에 남긴다. 기본값 파라미터(source_type="session",source_ref=session_id)라 기존 호출부는 무변경. Phase-2(URL ingest 등) 다양한 소스 유형을 받을 수 있도록 시그니처를 미리 열어둠. - 회수 시 provenance 부착 (
recall_memory): 회수 결과에 메모리 파일의 frontmatter를 재파싱해provenance(source_type / source_ref / captured_at)를 동반한다. (DB 스키마 불변 — 결과 path frontmatter 재파싱 방식, TOP_K=1이라 비용 무시 가능.) - 회수 출력 "출처:" 라벨 (
_format_output):- [name]라인 직후출처: {source_type} {ref8} {date10}짧은 한 줄을 삽입(ref·날짜는 앞 8·10자만, 토큰 낭비 방지). 노이즈 방지를 위해unknown·빈 source_type은 미표시. Layer 4 hook(hooks/memory-recall.py _format_output)과 compact 재주입(src/recall_core.py format_memory_context)은 별도 구현이지만, parity 테스트(test_formatter_byte_equivalence)가 두 경로의 출력이 byte-동일함을 강제한다(provenance 4 shape 포함 — datetime captured_at / unknown 억제 / None ref / 8자 초과 ref). - 기존 메모리 backfill CLI (
provenance_backfill_cli): 출처가 없는 기존 메모리에 소급 부여. 억측 금지 원칙 — 기록된 session id가 있으면session, 없으면unknownfallback(git blame류 추론 안 함). 실제 기록 형식을 모두 인식하도록 session id 우선순위를staged_from_session→originSessionId(top-level) →metadata.originSessionId(nested — memory-production 파이프라인의 실제 형식)로 확장._procedural/디렉토리 포함(recall 인덱싱 범위와 일치),_staged/·MEMORY.md제외, atomic write(tmp + os.replace), trailing-whitespace fence 파일도 crash 없이 처리. dry-run 기본 /--apply시에만 쓰기, 멱등, skip·실패 파일명 리포트.
Fixed (adversarial sweep)
- cmd_approve 승격 시 출처 소실 — 게이트1 프로덕션 갭 (핵심):
write_staged는 출처를 staged frontmatter에 박았지만,cmd_approve가 승격(promote) 시 frontmatter를 처음부터 재구성하며source_type/source_ref/captured_at를 통째로 떨어뜨려 — 완료 게이트 "신규 영구기억 출처 추적률 100%"를 정작 영구 진입 경로(/memory_review 승인)에서 깨고 있었다._provenance_passthrough헬퍼(_supersede_passthrough패턴 미러)를 NEW-PROMOTE / UPDATE 두 final_fm 블록에 모두 wire-in. UPDATE 경로는 기존 영구메모리의 원본 출처를 우선하고, absent(또는unknown)일 때만 staged 메타로 폴백 — donor를 원자적으로 선택해 incoherent한 source_type/source_ref 짝 섞임을 차단. - recall 핫패스 UnicodeDecodeError 견고화: provenance frontmatter 재파싱 루프가
(OSError, UnicodeDecodeError, KeyError)를 모두 catch — 비-UTF8/손상 메모리 파일 한 개가 recall 결과 전체를 [] 로 날리던 경로 차단(backfill CLI와 대칭). - source_ref str 정규화 (JSON-safe):
recall_memoryprovenance 루프에서source_ref를captured_at과 동일 방식으로 정규화(isoformat 우선, 그 외 비-None/비-빈은str(), None/"" 유지). YAML이 bare 숫자·날짜를 int/date로 파싱해도/recall의json.dumps가 깨지지 않도록 보장(+ recall_clijson.dumps(..., default=str)방어). - contradiction-aware writer kwargs forward (Phase-2 forward-proof):
make_contradiction_aware_writer의 wrapper가**kwargs를 base_writer로 전달하지 않아,source_type/source_ref를 넘기는 미래 호출자(Phase-2 URL ingest 등)가 writer를 통과하며 provenance를 silently drop할 latent 버그를 사전 차단. 기존 caller(kwargs 없음) 무영향, contradiction detection 로직 변경 없음. - backfill 견고화: ref 확정 시
re.sub(r"\s+", " ", str(ref))로 multiline 값을 단일 라인으로 collapse(frontmatter 구조 보호), trailing-ws 닫는 fence를 tolerant 탐지(exactlines.index("---")ValueError → 전체 run abort + --apply half-migration 차단), per-file try/except로 한 파일 실패가 배치를 중단시키지 않음.
Tests
- provenance end-to-end 통합(write_staged → cmd_approve 승격 → index → recall → format 출처 라벨), cmd_approve NEW/UPDATE/fallback·unknown-adopts-staged 커버리지, recall_core parity(provenance shape), JSON-safe 직렬화, trailing-ws fence·multiline-ref collapse 단언 등 +28 테스트.
- vacuous(실제로 fix를 검증하지 못하던) fixture 교정 — multiline collapse 경로를 강제 트리거하도록 fixture에 실제
\nembed, docstring 부정확 기술 정정. - 전체 회귀: 668 collected · 667 passed · 1 skipped(pre-existing) · 25 subtests passed.
Notes
- 기존 메모리 backfill은 별도 CLI(
python -m src.provenance_backfill_cli <memory_dir> [--apply])로 수행 — 운영 메모리 172건(session 83 / unknown 89) 소급 적용 완료. - Phase-1의 나머지 두 축(②효과적 회수 self-check, ③stale 자동 감지)은 별도 릴리즈로 분리.