회기 종료 pinned_fact upsert를 AI evaluator 뷰로 실행
공개 /sessions/{id}/end 503(2026-08-18 실측): app.pinned_fact RLS는 learner 역할에
INSERT(소유 케이스)만 허용하고 SELECT/UPDATE는 AI 컨텍스트 또는 admin/instructor
전용이다. end_session이 learner 연결로 SELECT FOR UPDATE + INSERT ON CONFLICT
DO UPDATE 조합을 돌려 충돌 후보 행 가시성이 막히며 InsufficientPrivilegeError로
실패했고, fallback 차단이 503으로 노출됐다. 엔진이 건강해 deep 평가가 처음으로
pinned 팩트를 반환한 순간 발화한 잠복 결함이다.
- _upsert_pinned_fact_candidates_with_ai_view: 같은 learner uid 로
acquire(ai_view='evaluator') 를 열어 INSERT/SELECT/UPDATE 정책을 모두 충족
- 검증: 공개 DB에서 롤백 재현 — learner 컨텍스트 upsert RLS 거부,
ai_view evaluator 컨텍스트 동일 SQL 통과. app 924 passed·관련 62 passed
This commit is contained in:
parent
6a42063d49
commit
bf5f7352ce
1 changed files with 16 additions and 3 deletions
|
|
@ -2896,9 +2896,22 @@ async def _insert_pinned_fact_history(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _upsert_pinned_fact_candidates_with_ai_view(sess: InProcSession) -> None:
|
||||||
|
"""pinned_fact upsert를 AI evaluator 뷰 컨텍스트로 실행한다.
|
||||||
|
|
||||||
|
app.pinned_fact의 RLS는 learner 역할에 INSERT(소유 케이스)만 허용하고
|
||||||
|
SELECT/UPDATE는 AI 컨텍스트 또는 admin/instructor로 제한한다. 그래서
|
||||||
|
learner 연결에서 SELECT … FOR UPDATE + INSERT … ON CONFLICT DO UPDATE 조합은
|
||||||
|
충돌 후보 행 가시성이 막혀 InsufficientPrivilegeError 로 실패한다
|
||||||
|
(2026-08-18 공개 /sessions/{id}/end 503 실측). 같은 learner uid 로
|
||||||
|
ai_view evaluator 컨텍스트를 열면 INSERT/SELECT/UPDATE 정책이 모두 통과한다.
|
||||||
|
"""
|
||||||
|
async with acquire(role="learner", user_id=sess.learner_id, ai_view="evaluator") as ai_conn:
|
||||||
|
await _upsert_pinned_fact_candidates(ai_conn, sess)
|
||||||
|
|
||||||
|
|
||||||
async def _upsert_pinned_fact_candidates(conn: Any, sess: InProcSession) -> None:
|
async def _upsert_pinned_fact_candidates(conn: Any, sess: InProcSession) -> None:
|
||||||
turn_rows = [
|
turn_rows = [ {
|
||||||
{
|
|
||||||
"speaker": turn.speaker,
|
"speaker": turn.speaker,
|
||||||
"text": turn.text_masked,
|
"text": turn.text_masked,
|
||||||
"turn_id": turn.turn_id,
|
"turn_id": turn.turn_id,
|
||||||
|
|
@ -3139,7 +3152,7 @@ async def end_session(sess: InProcSession, carry: memory.CarryOver) -> bool:
|
||||||
rapport_trajectory,
|
rapport_trajectory,
|
||||||
alliance_level,
|
alliance_level,
|
||||||
)
|
)
|
||||||
await _upsert_pinned_fact_candidates(conn, sess)
|
await _upsert_pinned_fact_candidates_with_ai_view(sess)
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("persisted session end failed session_id=%s", sess.session_id)
|
logger.exception("persisted session end failed session_id=%s", sess.session_id)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue