Repository navigation
Audit round 5: Home on failed reads, daily streak at app start, clean-ups - #6
Merged
Merged
Conversation
Home read progress with loadProgressFromCloud, which returns null both when there is no progress document and when the read failed, and drew a new student's defaults (0 XP, 1-day streak, 0 stages) for either. A dropped connection replaced a real student's dashboard with zeros. Home now reads with loadProgressResult. HomeFragment.homeViewFor decides: Loaded is drawn as before; Absent draws the new-student defaults as before; Failed keeps the figures Home last drew - kept in its own home_shown_* prefs, because the other home_* values also get LearningPathFragment's first-launch zeros - or, if Home never drew any, leaves the dashboard blank. A Snackbar says "Couldn't load your progress. Check your connection and try again." with Retry; pull-to-refresh retries too. Nothing is written to Firestore on this path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eTtYuuVWNwDn1mSsKbKrW
The streak was only updated when Learning Path was opened. After days away Home showed an old streak, and a student who only opened Home or Practice never got one. UserProgressSyncService.recordDailyVisit counts the day once per student per calendar day. MainActivity asks in onStart (so it also runs after the app sat in the background past midnight), and Home and Learning Path ask before they read progress, so whichever comes first does the update and the others wait for it and show the new streak. Learning Path no longer updates the streak itself. The rule is the old one, moved into dailyStreak: same day changes nothing, the next day adds 1, a gap of two or more days starts again at 1, and the best streak only goes up. The write is saveStreak's streak fields. A failed read writes nothing and is retried next time. A write Firestore hasn't confirmed after 3 seconds (offline) is counted as made - it is already in the local cache and is sent on reconnect - so it can't hold Home and Learning Path. The milestone and "streak broken" toasts move with it, shown by whichever caller made the update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eTtYuuVWNwDn1mSsKbKrW
- Delete FirestoreManager.deleteAllPersonalizedStages. Nothing calls it
since the retake moved to one WriteBatch, and it pointed at
users/{username}/personalized_stages, where no stages are kept.
- pathReadOf's KDoc sat above retakeWrites, attached to the wrong
function. Each comment is now on its own function.
- The quiz and challenge services wrote the model's reply text to
release-level logs, which survive R8: GeminiQuizService's "Raw response
(first 500 chars)" Log.e, GeminiChallengeService's two "Cleaned response"
Log.e lines, and a Log.i with the model-generated problem statement. They
now log the length (and, for the quiz, the parse error that was already
logged). Log.d and Log.v are untouched.
ReleaseLogIdentifierTest now also fails on any Log.w/e/i/wtf that
interpolates a reply-text variable (response, cleaned, cleanJson, content,
problemStatement, ...) other than its length, size or HTTP code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eTtYuuVWNwDn1mSsKbKrW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Round 5 of the audit, branched from master at 10900e3 (after #5 was merged). Same rules as before: one commit per fix (the three clean-ups share one, as you allowed), a JVM unit test for each fix, explicit
git addpaths, no amend or force-push. Test names avoid" : * ? < > |.This PR leaves the security-rules PR (#3) alone. It does not touch
firestore.rules,database.rules.json,firebase-rules-tests/or the docs that PR changes.Contents: 3 commits, in the order you gave them. For each one I reverted just the fix's logic (or, for the log guard, put master's files back), re-ran its tests, and confirmed they fail.
1. Changes
ui/home/HomeFragment.kt:261,:279,:289,:632,:643,:691,:699loadProgressFromCloudreturns null both when there is no progress document and when the read failed. Home drew a new student's defaults for either.loadProgressResult(), andhomeViewFordecides what to show:- Loaded: as now.
- Absent: the new-student defaults, as now.
- Failed: keeps the figures Home last drew. If Home never drew any, the dashboard stays blank; it never shows zeros.
A Snackbar says "Couldn't load your progress. Check your connection and try again." with Retry, and pull-to-refresh also retries. Nothing is written to Firestore on this path.Home keeps its own
home_shown_*copy of what it drew. The existinghome_*keys also get Learning Path's first-launch zeros, written before any read.HomeProgressTest(5)services/UserProgressSyncService.kt:99,:125,:140,:162,:411;MainActivity.kt:91;ui/home/HomeFragment.kt:143;ui/learningpath/LearningPathFragment.kt:471;AppDate.kt:38recordDailyVisitcounts the day once per student per calendar day, whichever caller asks first.- Home and Learning Path ask before they read progress. If the update is already running, they wait for it, so they show the new streak instead of racing the write.
- Learning Path no longer updates the streak itself.
The rules are unchanged, moved intoMainActivity.onStartasks for any signed-in student, including when the app comes back to the foreground after midnight.dailyStreak:- The same day changes nothing.
- The next day adds 1.
- A gap of 2 or more days starts again at 1.
- The best streak only goes up.
The write is stillsaveStreak's streak fields.DailyStreakTest(12)services/FirestoreManager.ktdeleteAllPersonalizedStageshad no callers and pointed atusers/{u}/personalized_stages, where no stages are kept.services/FirestoreManager.kt:120,:136pathReadOf's KDoc sat aboveretakeWrites.services/GeminiQuizService.kt:294;GeminiChallengeService.kt:266,:1151,:1181Log.e"Raw response (first 500 chars)".Log.e"Cleaned response" lines.Log.iwith the model-generated problem statement.Log.dandLog.vare untouched.ReleaseLogIdentifierTest(+2, now 4)On the tests
DailyStreakTestcovers:Log.w/e/i/wtfthat interpolates a reply-text variable:response,responseText,cleaned,cleanedResponse,cleanJson,rawResponse,reply,content,jsonArrayorproblemStatement..length,.sizeor.codeis allowed.$nameis always flagged, because"$cleaned.length"prints the text.homeViewFor,dailyStreak,recordDailyVisitandstreakMessageare tested directly. The Activity and Fragment lines that call them need Robolectric to exercise, so I checked them by reading the code: the Snackbar, thehome_shown_*cache, and theonStart, Home and Learning Path calls.2. Found but not fixed
services/GeminiChatService.kt:164,GeminiReportService.kt:732Log.ewrites the HTTP error body ($responseBody) on a non-2xx response. That is the gateway's error JSON, not a model reply, so it is outside 3c and the guard doesn't checkresponseBody. It is still server text in release logs.responseBodyto the guard.UserProgressSyncService.syncProgress,saveProgressToCloud;LearningPathFragment.syncProgressToCloudgetLocalProgress, which accounts for 4 of the 20 warnings (2 warnings, each reported twice).KeyPathCostHarness.kt:32,MisconceptionDiagnosticHarness.kt:168C:/Users/...path, which leavesapp/C:/in a Linux checkout. One was already sitting in this branch's working tree; I deleted it.build/.LoginActivity.kt:518,:589;QuizActivity.kt:183.!!inLoginActivity.GEMINI_API_KEYfield.Out of scope as you asked, so not started: individual contestant sign-in, a server-side username lookup, the event accounts, Analytics XP, partly generated quizzes, and rotation state on the retake screen.
3. Verification
Run on this branch's head after
./gradlew clean. JUnit XML totals: 224 tests, 0 failures, 0 errors, 0 skipped across 39 classes. That is master's 205 plus 19 new:HomeProgressTest(new)DailyStreakTest(new)ReleaseLogIdentifierTest(extended)Kotlin warnings: 20, the same as master (10 deprecation warnings, each reported for both the debug and release compiles). None are new.
compileReleaseKotlinpasses.Dependencies came from the repositories in
settings.gradle.kts, with no mirror; they were already cached.Generated by Claude Code