In the grader persistence path, record_ai_result() is called when either grade or feedback is present, but a missing grade results in score=0.0 being recorded (overwriting any existing draft score). Since the grader contract allows grade to be null, avoid calling record_ai_result unless grade is provided (or preserve the existing draft_score instead of forcing 0.0).
grade = item.get("grade")
feedback = item.get("feedback")
row.score = grade
row.feedback = feedback
row.graded_at = _utc_now()
row.grading_meta = {
**(row.grading_meta or {}),
"grading_metadata": item.get("metadata", {}),
}
# Only record an AI result when a concrete grade is provided.
# This avoids overwriting any existing draft score with a synthetic 0.0
# when extensions send feedback without a numeric grade.
if grade is not None:
manager.record_ai_result(
submission_id=submission.id,
score=float(grade),
feedback=feedback or "",
Originally posted by @Copilot in #176 (comment)
In the grader persistence path, record_ai_result() is called when either grade or feedback is present, but a missing grade results in score=0.0 being recorded (overwriting any existing draft score). Since the grader contract allows grade to be null, avoid calling record_ai_result unless grade is provided (or preserve the existing draft_score instead of forcing 0.0).
Originally posted by @Copilot in #176 (comment)