`785ebdb` failed at compileDebugKotlin with two `Unresolved reference 'dismiss'`.
Splitting the reminder notification code into its own object, I removed
`dismiss` from `Reminders` and never pasted it into `ReminderNotification`. The
call sites were correctly qualified; the function simply was not there.
All four local gates passed it, and `check-symbols.py` passed it for a reason it
documented about itself: it only resolved the LEADING segment of a dotted
expression, because that is the part a regex can resolve. `ReminderNotification`
existed, so `ReminderNotification.dismiss(...)` looked fine.
That was a real gap rather than an inherent one, so the checker now indexes the
members of every `object` declared in the package and verifies `Foo.bar` against
them. Brace-counted, not regex-matched — an object body is full of nested braces
from lambdas and apply blocks, and no regex closes correctly over them.
Verified by deleting `dismiss` from a copy of the tree again: it reports the
same two call sites the Kotlin compiler did. What it still cannot see is
narrowed and written down rather than left implied — members of anything
declared outside this package, members reached through a variable rather than a
type name, and every question about types.
`750d11d` failed CI at `compileDebugKotlin` with `Unresolved reference 'Build'`.
`defaultDeviceName()` reads `android.os.Build`, and the import was lost when
`SyncPairing.kt` was split out of `SyncScreen.kt`. One line to fix.
The interesting part is that ktlint and detekt had both passed it, locally and
in CI. Neither resolves symbols — they parse — so a file that cannot compile is
indistinguishable to them from one that can. A clean analyzer run is not
evidence the code builds, and on this repo `compileDebugKotlin` is the only
gate that type-checks at all, since there is no Android SDK on the workstation.
So: `android/tools/check-symbols.py`, covering that one blind spot. It flags any
capitalised identifier that is neither imported, declared in the same package, a
type parameter, nor implicitly available. Not a type checker and not pretending
to be — a pre-push filter for the single mistake that survives every other local
gate, erring toward false positives.
Verified against a known-bad tree rather than trusted on a green: deleting the
`Build` import from a copy makes it fail with the same two references the Kotlin
compiler reported. That step is not ceremony. An earlier attempt at this check
stripped line comments with `re.S`, where `//.*` eats each file from its first
comment to EOF — it examined almost nothing and reported everything clean.
ci-requirements.md now documents all three Kotlin checks, and its claim that no
workflow consumes the Android image yet is gone; the lane has been running since
step 5.