Guard that every drawable has a drawable-night twin #42
Labels
No Label
P0
P1
P2
release-blocker
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: null/Privacy-Period-Tracker#42
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The light/dark pairing is protected by a preview and nothing else.
What is true now
app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingPreviews.ktrenders all eight illustrations in a light/dark@Previewpair, with one entry per illustration, explicitly so that a missing night asset is visible. Its own KDoc says why: "a set of eight where seven have a night variant looks completely fine in light mode."But a preview is not a test. Nothing fails a build, there are no screenshot tests in the repository, and the three existing guards —
checkModuleBoundaries,checkPermissionsandcheckNoHealthLoggingin the rootbuild.gradle.kts— none of them inspect resources.What it costs
The theme swap is pure resource resolution, which is what makes it robust — and also what makes a gap silent. A missing
drawable-nightfile does not fail, does not warn, and renders the light illustration on a dark screen. Nobody sees it unless they open the app in dark mode on the one affected screen.What to do
Add
checkThemedDrawablesto the rootbuild.gradle.ktsbeside the other three, wired intocheckthe same way. Every file indrawable*/must have a counterpart of the same name in the matching-nightfolder, and vice versa.Write the guard and prove it fails before believing it passes.
docs/architecture/GUARDS.md§1 requires it andscripts/prove-guard.shperforms it. Three guards in this project have been confidently green over exactly the failure they claimed to catch — assume this one is broken until it has been watched failing.Traps
checkModuleBoundariesspent its first day green./build/and/bin/outputs.checkNoHealthLoggingfailed its own first run by reading a stale copy of a test file in a gitignoredbin/directory.ic_launcher_monochrome.xmlis a themed vector with no night variant and must not fail the build. Scope the rule to the illustration set, or allow an explicit exemption list with reasons.doLast, or the build fails with a cache error instead of a guard result.Verify:
bash scripts/prove-guard.shreports exactly one failure when adrawable-nightfile is deleted and green when it is restored, and./gradlew checkruns the guard.Done in
255808f.checkThemedDrawablesis in the rootbuild.gradle.ktsbeside the other three and wired into every module'scheck, verified in the task graph.It walks both directions — a light asset with no night twin, and a night asset with no light one. The second is the same defect from the other side, and it renders as nothing at all rather than as the wrong picture.
ic_launcher_monochromeis the single exemption, in a named map with its reason: the launcher tints it from the system palette, so a night copy would be a second source of truth for one shape. An exemption list rather than a narrowed scope, because a scope listing today's eight illustrations would not cover tomorrow's — and the defect being guarded against is a file somebody forgot.Proved to fail, four ways
prove-guard.shcannot drive this guard, and that is worth recording rather than working around: it breaks a target by replacing a string inside a file, and this guard's failure mode is a file that is not there, in a set that is entirely.webp. There is no string to replace in an absent file and no text in the present ones.So GUARDS.md §1's manual recipe was run instead, to the same standard:
drawable-night-xhdpi/art_welcome.webpdeletedart_welcomeart_orphan.webpaddedart_orphanthemedResourceRootspointed at a folder that does not existno drawables were found, so no pairing was checked. This is not a passThe fourth is the one this repo has been bitten by twice, so it gets proved too rather than assumed.
That limitation is now
GUARDS.md§9 — "some guards cannot be driven by prove-guard.sh, and must still be proved" — because the next guard whose subject is a missing file will hit the same wall, and the tempting response is to skip §1 rather than run it by hand.Traps from the issue, each addressed
/build/and/bin/excluded, the trapcheckNoHealthLoggingfell into on its own first run.ic_launcher_monochrome.xmldoes not fail the build — exempted with a reason rather than by narrowing the rule.doLastnever toucheslayout, which would capture theProjectand fail with a cache error instead of a guard result.