checkModuleBoundaries holds the dependency tables in docs/architecture/README.md
as a check: every module's permitted project dependencies, plus the rule that
domain:cycle and domain:prediction must never apply an Android plugin. core:ads
is already in the map with an empty permitted set, before the module exists —
PRODUCT_PLAN.md §34 is non-negotiable, and a guard written alongside the code it
constrains is one shaped around whatever exception somebody wanted at the time.
It lists every violation rather than the first, and refuses to report a pass
when it examined no modules at all.
THE GUARD FAILED ITS OWN FIRST PROOF
prove-guard.sh injected a forbidden dependency into :domain:prediction and the
guard reported "7 modules checked, no violations". The root project is
configured before its subprojects, so reading subprojects.configurations from
the root script saw every configuration empty — it had been green over an empty
map since the moment it was written, and would have been trusted for months.
Collection moved into afterEvaluate, and the task now throws rather than passing
if it ends up with no modules. Three proofs recorded in the architecture doc,
all re-run and all red: a domain module reaching upward, :app reaching past the
repository straight to Room, and a module with no rule being reported as
unmeasured rather than assumed fine.
TWO REAL BUGS FROM WIRING IT INTO `check`
Running the whole check for the first time turned up Android lint errors that
would have shipped:
NewApi: java.time.LocalDate#ofInstant requires API 34 (minSdk is 26)
NewApi: java.time.LocalDate#EPOCH requires API 34 (minSdk is 26)
Both are on the recalculation path. On any device below Android 14 — most of
the install base this app targets — that is a crash. Neither the unit tests nor
the API 36 emulator could see it; lint is the only thing that could.
Replaced with atZone().toLocalDate() and ofEpochDay(0), which are API 26.
Also cleared the lint warnings that were real: a redundant activity label, and
a round launcher icon declared but never referenced. The two that remain are
deliberate and now say so where the warning is read — targetSdk 36 is Play's
floor and raising it opts into untested runtime behaviour, and the -v26 mipmap
qualifier stays because removing it makes AAPT fail to resolve the icon at all.
./gradlew check now passes with 0 lint errors across all seven modules.
70 unit tests, all passing.
closes#7