feat(F-SCR-001): 판정이 근거 루브릭의 검토 상태를 들고 나간다 (#609 ① ⓑ · 계약 승인 대기) - #623
yoonjiseok wants to merge 1 commit into
Conversation
❗**계약 승인 대기 중이다 — `contracts/judgment.schema.json` 을 안 건드렸다.** 이 커밋은 ai-service 쪽만이고, 계약·Java 레코드는 강희진 몫이다(CLAUDE.md — 오너 승인 없이 변경 금지). 그래서 draft 로 낸다. ## 왜 `status: confirmed` 는 「근거자료 검토를 마쳤다」는 뜻인데 그 값을 읽는 코드가 채점 경로에 없었다(#617 이 기동 로그와 못 박기로 드러냈다). 실물은 ELS 10종 confirmed · 변액 7종 **전부** draft 다 — S-02 에서 변액을 고르면 그 세션의 이해항목 전부가 검토 전 기준으로 채점된다. 채점은 안 바꾼다. draft 를 `RubricNotFound` 로 빼면 「승인했는데 안 돈다」가 되고(#475 ⓐ 의 승인 흐름이 draft 로 커밋하는 것이라 첫 단계가 막힌다) 데모 채점도 바뀐다. 대신 **판정이 그 사실을 들고 나가** 화면·교부 문서가 말할 수 있게 한다. ## 무엇 - `Judgment.rubric_status: Literal["confirmed","draft"] | None` - `scoring._pin_rubric_status` — `_pin_prompt_version` 옆. **모델에게 안 묻는다**: 루브릭 파일이 무엇인지는 우리가 아는 사실이고, 모델이 채우게 하면 파일을 바꿔도 아무 일이 안 일어난다(결정 10.46 과 같은 논거) - ❗**없으면 confirmed 로 읽지 않는다.** `source` 는 「없으면 MEASURED」인데 여기는 반대다 — 기본값을 confirmed 로 두면 이 필드 이전 레코드 전부가 「검토된 기준으로 판정했다」가 된다. 그게 이 필드를 만드는 이유 자체를 지운다(#284 의 「빈 것 ↔ 없는 것」) `Judgment` 는 LLM 구조화 출력 스키마이기도 해서 모델이 이 칸을 채워 보낼 수 있다 — 핀이 이긴다는 것을 단정으로 박았다. `strict` 자격은 안 바뀐다(이미 False · 선택 필드가 있다). ## 변이 역검증 ⓐ 핀을 사슬에서 뺀다 4 failed ⓑ 루브릭이 아니라 모델 값을 쓴다 4 failed ⓒ 기본값을 confirmed 로 둔다 1 failed ⓓ 항상 confirmed 를 박는다 2 failed ← 두 상태가 안 갈리는 것을 문다 ai-service 1143 passed · skip 0 Refs #609 · #617 · #475 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
계약 승인합니다. 필드 이름·값·optional·「없으면 confirmed 로 읽지 않는다」 넷 다 그대로 갑니다.
❗머지 순서가 제약입니다 — 이 PR 이 먼저 들어가면 채점이 통째로 깨집니다재 봤습니다. 서버가
그래서 제 쪽(계약 + Java 레코드)이 먼저 들어가야 합니다. 제가 지금 끌겠습니다 — 오늘 올립니다. ①만 들어간 상태는 안전합니다 — ai-service 가 아직 안 실으니 값이 계속 나가는 쪽은 안 따라 늘어납니다 — 확인했습니다
❗하나 물어봅니다 — 룰이 만든 판정의
|
|
승인 감사합니다. 순서에도 동의합니다 — 제 PR 이 먼저 들어가면 채점이 깨지는 것 맞습니다.
①만 들어간 상태가 안전하다는 것도 맞습니다 — 값이 계속 ❗물어보신 것 — 동의합니다. 다만 값 이름 하나와 셋째 경우가 있습니다①
|
|
선행을 올렸습니다 — 정정 하나 — 위에서 제가 그 문장을 계약 이번에 그물도 같이 달았습니다이 순서 문제가 아무 테스트에도 안 걸렸던 것이 진짜 결함이라, 다음에 그쪽이 계약에 칸을 만들면 제 쪽 CI 가 먼저 알려줍니다 — 배포에서 만나지 않고. 담는 자리도 정했습니다
|
|
곁가지로 하나 — 여기서 순서가 생긴 이유는 도메인 레코드에 없는 키를 보내면 // AiServiceClient:581
public record Warning(String code, String itemId, String message) {}
지금 상태만 정리하면 이렇습니다. |
Important
draft 입니다 — 계약 승인 대기 중입니다.
contracts/judgment.schema.json을 안 건드렸습니다(CLAUDE.md — 오너 승인 없이 변경 금지). 이 PR 은 ai-service 쪽만이고, 계약·Java 레코드는 @hd0rable 몫입니다. 결정을 구체적으로 보시라고 코드를 먼저 올립니다.#609① 의 뒤 절반입니다. 앞 절반(#617)은 머지됐습니다 — 그건 사실을 드러내기만 했고, 이건 판정이 그 사실을 들고 나가게 합니다.무엇이 문제인가
status: confirmed는 "근거자료 검토를 마쳤다" 는 뜻인데 그 값을 읽는 코드가 채점 경로에 없습니다.S-02 에서 고를 수 있는 두 상품 중 하나를 고르면 그 세션의 이해항목 전부가 검토 전 기준으로 채점됩니다(@hd0rable 이
#617리뷰에서 짚은 규모입니다). 그런데 판정 레코드에는 그 사실이 안 남습니다 — 검토 전 기준으로 낸 판정이 확정 기준 판정과 똑같이 생겼고,evidence/가 append-only 라 나중에 되짚을 수 없습니다.왜 ⓐ 가 아닌가
무엇을 했나
모델에게 안 묻습니다. 루브릭 파일이 실제로 무엇인지는 우리가 아는 사실이고, 모델이 채우게 하면 파일을 바꿀 때 그 문면을 같이 안 고쳐도 아무 일이 안 일어납니다 —
prompt_version과 같은 논거(결정 10.46)입니다.❗
Judgment는 LLM 구조화 출력 스키마이기도 해서(complete_json(model_cls=Judgment)) 모델이 이 칸을 채워 보낼 수 있습니다. 핀이 이긴다는 것을 단정으로 박았습니다.strict자격은 안 바뀝니다 —Judgment는 이미False이고(선택 필드가 있어서) 하나 더 추가해도 같습니다(test_structured_output_strict초록).❗없으면
confirmed로 읽지 않습니다이 자리가
source와 반대입니다.기본값을 confirmed 로 두면 이 필드가 생기기 전 레코드 전부가 「검토된 기준으로 판정했다」로 읽힙니다. 실제로는 모르는 것이고, 그게 이 필드를 만드는 이유 자체를 지웁니다 —
unlinked_until의 「빈 것 ↔ 없는 것」(#284)과 같은 자리입니다.우리 쪽은 항상 싣습니다(
rubrics.get()이 준 값이라None이 될 수 없습니다). optional 인 것은 옛 레코드 때문이지 비워도 된다는 뜻이 아니라, 그것도 단정으로 뒀습니다.변이 역검증
confirmed로 둔다confirmed를 박는다#617의 못 박기(test_scoring_cannot_tell_the_two_apart— "채점 입력이 두 상태에서 글자까지 같다")는 그대로 초록입니다. 이 PR 이 바꾸는 것은 입력이 아니라 출력이라 그 단정과 안 부딪힙니다. 그 관계를 새 테스트 독스트링에 적어 뒀습니다.계약 쪽 — 승인 주시면 이 모양입니다
제가 파일을 안 고쳤으니 초안만 둡니다.
required는 안 건드립니다(['item_id','grade','confidence','evidence','reason']그대로). JavaJudgment레코드에String rubricStatus한 칸이promptVersion옆에 붙습니다.수요자
#617리뷰에서 "필드를 항목마다 두는 것이 맞다고 봅니다 … 계약은 넓게 두고 화면이 뭉쳐 보여 주는 쪽이 순서가 낫습니다" 라고 하신 그 모양입니다. ❗P1 을 넘지 않게 이 값이 판정에 직접 들어가지 않습니다 — 룰이 쓸 거면gate_rules.yaml에서 명시적으로 쓰는 쪽입니다evidence/· 교부 문서. 이 필드 없이 쌓인 레코드는 나중에 복원이 안 됩니다 — 그게 지금 내는 이유입니다검증
Refs #609 · #617 · #475
🤖 Generated with Claude Code