MOB-104: mix mob.doctor detects the stale sheet-dismissal wire shape - #48
Merged
Conversation
decisions/2026-08-25-detect-dont-autopatch-native-source.md:39 says any future
"template fixed, existing apps still broken" situation should follow the same
shape as the MOB-98 check: a pure detection kernel plus a mix mob.doctor
warning, because MobBridge.kt is app-owned and never re-rendered. MOB-104 is
exactly that situation.
An app carrying the pre-fix sheet renderer routes dismissal through
MobBridge.nativeSendTap and delivers {:tap, tag}. Mob.UI.sheet/2 documents
:on_dismiss as {:dismiss, tag} and iOS has always sent that, so a screen written
to the contract never matches: Mob.Screen forwards the unmatched message to
handle_info, raising FunctionClauseError and killing the screen — or it is
silently dropped by a catch-all, and the BEAM never learns the sheet closed so
it can't be re-presented. Harder to diagnose than MOB-98, which at least failed
loudly with UnsatisfiedLinkError.
Gated on the sheet renderer being present (`fun MobSheet(`), so an app
generated before Mob.UI.sheet/2 existed is not warned about — it has no sheet
support to be wrong about. Worth noting the blast radius is narrower than it
first looks: no PUBLISHED mob_new ships MobSheet yet (it is unreleased on
master), so this can only affect apps generated from mob_new master — dev boxes
and agent-generated test apps — not anyone installing from Hex. The check earns
its place going forward, once 0.4.24 puts the renderer in real projects.
Detection deliberately matches `nativeSendDismiss` as a bare substring rather
than a declaration shape: the MOB-98 kernel already had to be rewritten once
because a one-line String.contains? reported a correctly hand-ported fix as
broken forever (doctor only warns and never re-checks itself), and the same trap
is covered here by a split-annotation test.
Verified against real template output, not just fixtures: origin/master's
MobBridge.kt.eex flags true, the MOB-104 branch's flags false, and a
0.4.23-generated app's rendered MobBridge.kt flags false. 13 doctor tests,
format and credo --strict clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The check would otherwise land on master undocumented and ship in whatever release comes next with no note — the same changelog/release seam that left mob 0.7.30 with empty GitHub release notes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
decisions/2026-08-25-detect-dont-autopatch-native-source.md:39says any future "template fixed, existing apps still broken" situation should follow the MOB-98 shape: a pure detection kernel plus amix mob.doctorwarning, becauseMobBridge.ktis app-owned and never re-rendered. MOB-104 is exactly that situation, and an adversarial review flagged its absence as the one thing to gate the release on.What it catches
An app carrying the pre-fix sheet renderer routes dismissal through
MobBridge.nativeSendTapand delivers{:tap, tag}instead of the documented{:dismiss, tag}. Harder to diagnose than MOB-98, which at least failed loudly withUnsatisfiedLinkError— this delivers a plausible-but-wrong message that kills the screen withFunctionClauseError, or gets silently swallowed.Scope, honestly
Narrower than it first looks: no published
mob_newshipsMobSheet(it's unreleased on master), so today this can only affect apps generated frommob_newmaster — dev boxes and agent-generated test apps — not anyone installing from Hex. The check earns its place going forward, once 0.4.24 puts the renderer in real projects.Design notes
fun MobSheet(being present, so an app generated beforeMob.UI.sheet/2existed isn't warned about — it has no sheet support to be wrong about.nativeSendDismissas a bare substring rather than a declaration shape, deliberately. The MOB-98 kernel already had to be rewritten once because a one-lineString.contains?reported a correctly hand-ported fix as broken forever —mob.doctoronly warns and never re-checks itself. A split-annotation test covers that trap here.check_build/0's aggregate, not just defined (an unwired check has been a bug here before).Verification
Against real template output, not just fixtures:
origin/master'sMobBridge.kt.eexflags true, the MOB-104 branch's flags false, and a 0.4.23-generated app's renderedMobBridge.ktflags false. 13 doctor tests,mix format,mix credo --strictclean.Docs-and-detection only — no behaviour change, no version bump.
🤖 Generated with Claude Code