Skip to content

Fix target-grade simulator: make achievability check rounding-consistent#13

Merged
veniplex merged 1 commit into
mainfrom
claude/materials-file-upload-features-dpmieb
Jul 13, 2026
Merged

Fix target-grade simulator: make achievability check rounding-consistent#13
veniplex merged 1 commit into
mainfrom
claude/materials-file-upload-features-dpmieb

Conversation

@veniplex

Copy link
Copy Markdown
Owner

The dashboard "what do I need?" (Zielnote) simulator reported a target as unreachable in a case where the best achievable final grade rounds to exactly that target.

Problem

The simulator compared the required remaining average against the raw target, but every grade in the app is displayed rounded to one decimal. Example (real user data): 60 ECTS graded at a 2.0 average, 60 ECTS remaining, target 1.5.

  • To land on 1.5 you need a 0.99 average on the remaining modules — below the best grade (1.0), so it was flagged "unreachable".
  • But the best achievable final is 1.504, which the app displays everywhere as "1,5". So a student who scores straight 1.0s would see a final Schnitt of "1,5" on the dashboard while the goal simulator had said it was impossible — an internal inconsistency.

(The raw math itself was correct: a 1.2 average on the remainder yields a 1.6 final, not 1.5 — 1.2 is actually the requirement for a 1.6 target.)

Change

  • Extracted the logic into a tested pure function requiredGradeForGoal in src/lib/grades.ts and round the required grade to one decimal before classifying it, matching the rest of the app.
  • The borderline case now reads "you need a 1.0" instead of "unreachable"; safe/unreachable thresholds reuse the existing BEST_GRADE / PASS_THRESHOLD constants.
  • src/components/learn/grade-goal.tsx now calls the shared function instead of computing inline.

Verification

  • New unit tests in grades.test.ts cover the exact reported scenario (1.5 → needed 1.0; 1.6 → needed 1.2), plus truly-unreachable, safe, and no-remaining-ECTS cases. Full grade suite green (26 tests), tsc --noEmit and eslint clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LWLUNyHYuCpouMEUo3P456


Generated by Claude Code

The "what do I need?" simulator compared the required remaining average to the
raw target, while every grade in the app is displayed rounded to one decimal.
As a result a target whose best achievable final rounds to it (e.g. a best case
of 1.504, shown everywhere as "1.5") was reported as unreachable, even though
the dashboard would display the achieved grade as exactly the target.

Extract the logic into a tested pure function `requiredGradeForGoal` and round
the required grade to one decimal before classifying it, so the simulator stays
consistent with the rest of the app (the borderline case now reads "you need a
1.0" instead of "unreachable").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWLUNyHYuCpouMEUo3P456
@veniplex veniplex added the release-candidate Creates a new release and deployment of docker image, once merged. label Jul 13, 2026
@veniplex
veniplex merged commit 3c60b87 into main Jul 13, 2026
1 of 2 checks passed
@veniplex
veniplex deleted the claude/materials-file-upload-features-dpmieb branch July 13, 2026 20:18
github-actions Bot added a commit that referenced this pull request Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-candidate Creates a new release and deployment of docker image, once merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants