Skip to content

Render mob_plugins.exs with sorted map keys (MOB-383) - #140

Merged
GenericJam merged 2 commits into
masterfrom
mob-383-manifest-sort
Oct 15, 2026
Merged

GenericJam merged 2 commits into
masterfrom
mob-383-manifest-sort

Conversation

@GenericJam

Copy link
Copy Markdown
Owner

Fixes Linear MOB-383.

Problem

mix mob.regen_plugin_manifest --check compares priv/generated/mob_plugins.exs byte-for-byte with RuntimeManifest.render/1. render/1 used inspect(..., pretty: true), and on OTP 26+ a map prints its keys in atom-creation order, which differs between VMs. The file a deploy wrote and the one --check / mix mob.doctor re-rendered could differ in key order alone, so --check reported drift right after a regen.

Fix

render/1 passes custom_options: [sort_maps: true]: every map, nested ones included, prints with sorted keys. The device evaluates the file, so key order has no effect on what it reads.

Test

runtime_manifest_test.exs, "a manifest written by a VM with another atom order passes the --check comparison": a fresh elixir VM creates every atom in reverse order and writes a manifest with every map-bearing section (screens, lifecycle, settings, notification_handlers, composites, styles/theme, default_font). The test VM then runs the check's byte comparison. It fails on master (the keys come out in a different order) and passes with the fix.

Smoke: the committed mob_plugins.exs of 6 local apps (bezalel, chopaat, livebook_mob, mishka_showcase, mob_demo, mob_deliver_hello), each evaluated and rendered in two VMs with different atom-creation orders, came out byte-identical.

Audit: other generated files that print maps

I found no other committed or byte-compared file rendered from an inspected map or from map iteration:

  • TrustStore.trust_stanza/1 already sorts.
  • AppConfig uses term_to_binary(..., [:deterministic]).
  • driver_tab_* renders in the order of the mob.exs list.
  • NifActivation.render_record/1 iterates a fixed list of platforms.
  • The mob.local.exs stanza and beam_flags inspect only strings and keywords.
  • Merge.plist_keys/1 has string keys, and nothing compares its output.

AGENTS.md now has a gotcha for this.

Upgrading: the first regen rewrites an existing committed mob_plugins.exs with its keys reordered and no content change. Commit that once.

Review

Reviewer subagent (Codex rate-limited until 12:17 PM today): verdict to follow.

RuntimeManifest.render/1 printed the manifest with inspect, whose map key
order follows atom-creation order on OTP 26+. A VM that created the atoms in
another order (deploy vs doctor/--check) rendered different bytes, so
`mix mob.regen_plugin_manifest --check` reported drift right after a regen.
Print every map with sorted keys (custom_options: [sort_maps: true]).

The regression test writes a manifest with every map-bearing section from a
fresh VM that creates every atom in reverse, then runs the check's byte
comparison in the test VM.
@GenericJam

Copy link
Copy Markdown
Owner Author

Review: reviewer subagent (Codex rate-limited until 12:17 PM today). Round 1: MERGE with two test-robustness nitpicks (ebin via :code.which breaks under --cover; elixir lookup unguarded and path parsed from merged stdout). Both fixed in d5b2173. Round 2: MERGE, no findings. CI tests green on d5b2173 (run 38774861826).

@GenericJam
GenericJam merged commit c39abc2 into master Oct 15, 2026
6 checks passed
@GenericJam
GenericJam deleted the mob-383-manifest-sort branch October 15, 2026 17:11
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