Skip to content

MOB-104: mix mob.doctor detects the stale sheet-dismissal wire shape - #48

Merged
GenericJam merged 2 commits into
masterfrom
fix/mob-104-doctor-sheet-dismiss
Aug 27, 2026
Merged

MOB-104: mix mob.doctor detects the stale sheet-dismissal wire shape#48
GenericJam merged 2 commits into
masterfrom
fix/mob-104-doctor-sheet-dismiss

Conversation

@GenericJam

Copy link
Copy Markdown
Owner

decisions/2026-08-25-detect-dont-autopatch-native-source.md:39 says any future "template fixed, existing apps still broken" situation should follow the MOB-98 shape: 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, 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.nativeSendTap and delivers {:tap, tag} instead of the documented {:dismiss, tag}. Harder to diagnose than MOB-98, which at least failed loudly with UnsatisfiedLinkError — this delivers a plausible-but-wrong message that kills the screen with FunctionClauseError, or gets silently swallowed.

Scope, honestly

Narrower than it first looks: no published mob_new ships MobSheet (it's unreleased on master), so today 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.

Design notes

  • Gated on fun MobSheet( being present, so an app generated before Mob.UI.sheet/2 existed isn't warned about — it has no sheet support to be wrong about.
  • Matches nativeSendDismiss as a bare substring rather than a declaration shape, deliberately. The MOB-98 kernel already had to be rewritten once because a one-line String.contains? reported a correctly hand-ported fix as broken forevermob.doctor only warns and never re-checks itself. A split-annotation test covers that trap here.
  • Wired into 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'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, mix format, mix credo --strict clean.

Docs-and-detection only — no behaviour change, no version bump.

🤖 Generated with Claude Code

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>
@GenericJam
GenericJam merged commit 20ce8cb into master Aug 27, 2026
3 checks passed
@GenericJam
GenericJam deleted the fix/mob-104-doctor-sheet-dismiss branch August 27, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant