Skip to content

Refuse an AOD screen with no thumbnail, count Pro 3 slots, let a gone dial go - #84

Merged
freethinkel merged 2 commits into
mainfrom
issue-49-pro3-aod-slots
Oct 20, 2026
Merged

freethinkel merged 2 commits into
mainfrom
issue-49-pro3-aod-slots

Conversation

@freethinkel

@freethinkel freethinkel commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Refs #49. This covers the four points from redoxahmii's latest comment, plus a follow-up on #78's image-size cap (section 5). Two are fixed on evidence, one is fixed on the reporter's count, and one is unverified (details below).

1. AOD crash loop on the Watch 3 Pro

I downloaded both market files and compared their AOD screens with each other and with every stock AOD screen. The parser is the one in core/format.

AOD screen opens with a 0x28 preview
6vkrot44d1oxb19 (Pipboy, crashes) no. AOD is [group, full-screen image]
f4uj4uqrpefo4f2 (CR7, works) yes
Pro 3 stock (Pro_3_Watchfaces.zip) 129 / 129 (one face has no AOD)
Pro 2 stock (100 faces) 94 / 94 (six have no AOD)

None of the other features I checked separates them. Full-screen AOD images are normal in stock (86 of 129 Pro 3 AODs have one, and cf 5 appears there too). Groups appear in 104 stock AODs. The editor lets you delete the Preview layer in the tree, and that is how a face ends up like Pipboy.

What changed:

  • assertFlashable, the one gate before every install (editor and marketplace), refuses a file whose AOD screen has no 0x28. The message tells the user to open the face in the editor and flash it from there.
  • The editor's export (binOf → withPreviews) puts a blank preview on any screen that lacks one and bakes it like the others, so that route fixes the face.
  • Main screens without a preview are restored on export but not refused. They are just as far from the corpus, but nothing has crashed on one.

Blast radius: 24 of the 501 marketplace files have an AOD screen without a preview and are now refused on direct install. Some of them were already refused for image size.

Unverified, marked ponytail: in bin.ts: the cause is inferred from a single crashing file. Pipboy's AOD also has a group frame at y=282 that is 466 px tall, reaching 282 px below the panel. No stock face has a group more than 3 px off-panel. If the reporter still sees the loop after re-flashing from the editor, check that next.

2. Device info on connection: not fixed, unverified

Name, battery, firmware and serial are what the watch reports: fwRet bytes joined with dots, the serialRet string, and battery from 0x2a19/0x005c. The screenshot shows CMF Watch 3 Pro-9A9D, 100 %, 1.0.1.77, CI0420…, and I can't tell which of these is wrong without the watch's own settings screen to compare. The parts of that screenshot that are provably wrong are the dial list and the 6/6 (points 3 and 4). The draft reply asks which field differs.

3. Stale slot list

The list is the a055 notification the watch sends at connect, plus local claims for dials flashed from this browser (a055 leaves side-loaded dials out; mergeDials). Only an FMC overwrite or a refusal from the watch drops a claim, and FMC never hears about a deletion on the watch or in Nothing X. That is why Pipboy was still listed. Dials flashed from here now get a small remove button in the watch panel (dialRemoved → unclaimDial + mergeDials). Removing one that is actually still on the watch costs at most one refused upload, which then lands in the slot dialog.

4. Slot count

capacityOf(name): 7 for a Watch 3 Pro / Watch Pro 3 by advertised name, 6 otherwise. It is used by the pre-flight gate, the panel header and the troubleshooting page. The 7 is the reporter's count after a factory reset, not measured here (ponytail: in ble.ts). The 07 in the a055 header may be the watch reporting this itself, which needs a second sample to confirm.

5. Image size: confirm instead of refuse

The 480 px cap from #78 refused a factory Pro 3 face, Gravity_well_1753771909031_WatchFace.bin (576×530), so it was stricter than what the firmware accepts. Every other stock face, Pro 2 and Pro 3, stays at or under 470 px.

  • MAX_IMAGE_SIDE is now 580, a hard refusal as before.
  • Between SAFE_IMAGE_SIDE (470) and 580, flashFx holds the upload and a new dialog (size-dialog.svelte) explains the risk. Nothing goes to the watch until the user presses "Install anyway". Closing or cancelling drops the flash. Accepting marks the pending payload, so a full watch's slot-dialog retry doesn't ask again.
  • On the marketplace (501 files): 353 install as before, 74 now ask first, about 49 are still refused for size (down from about 105), and 24 are refused for a missing AOD thumbnail. Stock: Pro 2 100/100 pass, Pro 3 129 pass and Gravity_well asks.

Neither number is a measured firmware limit. The ponytail: on MAX_IMAGE_SIDE still applies.

Checks

  • npm run check: 0 errors. oxlint and oxfmt --check are clean on the touched files.
  • New tests: tests/flashable.test.ts (an AOD without a preview is refused, and withPreviews makes the rebuilt file pass; 576×530 passes the gate but needs a confirmation), tests/flash-size-confirm.browser.test.ts (the model holds an oversized flash until sizeConfirmed, and a decline sends nothing) and tests/watch-dials.browser.test.ts (capacityOf).
  • npm test: 321 passed, 0 failed (57 files). Earlier runs had flaky menu-animation failures under load, and they didn't recur here.
  • Gate run outside the suite: Pro 2 stock 100/100 pass, Pro 3 stock 130/130 pass (Gravity_well asks for confirmation), and tests/__fixtures__ 16/16 pass.

Draft reply to the reporter

@redoxahmii thanks, this was precise enough to act on.

AOD crash. I compared Pipboy with CR7 and with every factory AOD screen, 130 Pro 3 faces and 100 Pro 2 faces. Every factory AOD screen carries a small thumbnail, CR7 has one, and Pipboy's AOD doesn't. The editor let that layer be deleted, which is how it went missing. FMC now refuses to install a face like that, and when you flash from the editor it puts the thumbnail back. So for Pipboy and the other two that crashed: open them in the editor and flash from there. I can't test this on a Pro 3, so it's my best lead, not a confirmed fix. If you try it, I'd like to know whether AOD survives. If it still loops, Pipboy has one more odd thing in its AOD (a group that runs off the bottom of the screen), and that's next on the list.

Slots. FMC now counts 7 for a Watch 3 Pro, from your count after the reset. It also can't see a face you delete on the watch or in Nothing X, because the watch doesn't report faces installed from outside Nothing X. That's why Pipboy stayed in the list. Faces flashed from FMC now have a small × in the watch panel to drop them from the list.

Device info. Battery, firmware and serial are shown as the watch reports them. Which one is wrong compared with the watch's own About screen or Nothing X? With the right value I can find the parsing bug.

🤖 Generated with Claude Code

https://claude.ai/code/session_018vSR9vwRaYBqnq7ioL9dQ1

freethinkel and others added 2 commits October 20, 2026 16:08
… dial go

Three of the four open points in #49:

- The FMC face that crash-loops a Watch 3 Pro on AOD (market 6vkrot44d1oxb19) has
  an AOD screen with no 0x28 preview node. Every stock AOD screen has one: 129/129
  on the Pro 3, 94/94 on the Pro 2. The face reported as working (f4uj4uqrpefo4f2)
  has one too. assertFlashable now refuses such a file before any install, and the
  editor's export puts a blank preview back (withPreviews) and bakes it, so opening
  the face in the editor and flashing it from there fixes it. The tree lets the
  Preview layer be deleted, which is how a face loses it.
- The slot count is per model: 7 on a Watch 3 Pro, as the reporter counted after a
  factory reset, 6 otherwise. Neither number is measured here.
- A dial flashed from this browser stays in the watch list until FMC itself
  overwrites it. a055 never lists side-loaded dials, so the list only knows them
  from a local claim, and deleting one on the watch can't reach that claim. Those
  dials now get a remove button that drops the claim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018vSR9vwRaYBqnq7ioL9dQ1
The 480 px cap from #78 refused a factory Watch 3 Pro face (Gravity_well,
576x530), so it was stricter than what the firmware accepts. Every other stock
face, Pro 2 and Pro 3, stays at or under 470.

The hard limit is now 580. Between 470 and 580, flashFx holds the upload and the
new size dialog says what the risk is. The face goes to the watch only after
"Install anyway". Accepting marks the pending payload, so a full watch's slot
dialog retry doesn't ask again. Closing the dialog any other way drops the flash.

On the marketplace this turns about 56 refusals into confirmations. Faces
over 580 are still refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018vSR9vwRaYBqnq7ioL9dQ1
@freethinkel
freethinkel force-pushed the issue-49-pro3-aod-slots branch from fb2e843 to 8eac0ca Compare October 20, 2026 13:26
@freethinkel
freethinkel merged commit 5ffcf80 into main Oct 20, 2026
1 check passed
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