What broke
Home-screen icon missing: the page icon was not the cause
Our lottery statistics app shipped with an icon that showed in the browser tab but not on Henning's phone home screen. The icon had only been set as Streamlit's page icon, which covers the tab, and Streamlit does not serve a web app manifest. The fix was checked during the build and over HTTP, not just in the source.
The short version
What went wrongThe icon was set only as Streamlit's page icon, which covers the browser tab. The page had no manifest or apple-touch-icon, so the phone's home screen had nothing to pin.
How we found itThe icon failed to appear on Henning's phone, which opened a follow-up task. Its root-cause analysis looked at where home-screen icons actually come from.
The rule nowVerify in the built result, not just in the source. Check the patched index.html in the build and fetch the real manifest over HTTP.
This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes
This is a small failure, but the lesson from it applies beyond icons. We added an app icon, checked it, and marked the task done. Then the icon did not show up where Henning was looking for it: on his phone’s home screen.
The app is a statistics dashboard about lottery draws. It is built with Streamlit, runs in Docker and sits behind Cloudflare Access. Claude keeps the task log in the repository. The record below comes from that log and from one fix commit dated 4 July 2026.
What shipped first
The first task, covering a mobile-friendly UI and a PWA icon, was marked done on 4 July 2026. According to the task log, it included:
- an icon (a gold lottery ball on the app’s violet background), drawn as an SVG and exported to 192 and 512 pixel PNGs, sized to be safe for maskable use
- serving those files through Streamlit’s static file serving
- using the same icon as the favicon
- a check in a 390×844 touch viewport with Playwright, plus 120 passing tests
All of that was true. None of it put an icon on a phone’s home screen.
What went wrong
On the same date, 4 July, the log records a follow-up task, opened after the home-screen icon failed to appear for Henning. The root cause is written into both the task entry and the commit message.
The icon had been wired up as Streamlit’s page icon, and the page icon only sets the browser tab. Home-screen icons never come from the favicon. Streamlit does not serve a manifest or an apple-touch-icon link, so the page had neither. The phone had nothing to pin.
The icon files sat on the server, ready to be served. Nothing in the page told the phone they existed.
The commit message names the gap directly: Streamlit does not serve a manifest itself, and page_icon applies only to the tab, which it calls “the root cause of the missing home-screen icon”.
Who got what wrong should be stated carefully. The first task’s entry, written in the log Claude maintains, called the PWA icon done. Neither the log nor the commit uses the word “mistake” or says who made the wrong assumption. What the record does show is that the task was declared finished before this gap was found.
How it was found
The icon failed to appear for Henning, and that is what opened the follow-up task. The root-cause analysis there went past the visible symptom (“the icon is wrong”) to where home-screen icons actually come from.
The fix
The fix has two parts.
First, a manifest file now sits next to the icons in the app’s static folder. It sets the app name, standalone display mode, the theme and background colour, and three icon entries: 192 and 512 pixels for normal use, plus the 512-pixel image marked as maskable.
Second, the Docker build patches Streamlit’s own index.html. We cannot change the head through Streamlit’s normal settings, so a build step inserts these just before the closing head tag:
- a manifest link
- an apple-touch-icon link
- an Apple meta tag that lets the app run as a home-screen app, plus an app title
- a theme-color meta tag
One detail mattered because of Cloudflare Access. The manifest link carries a crossorigin attribute set to use credentials. Without it, the browser fetches the manifest without the Access cookie and gets the login page’s HTML back instead of JSON. The task log and a comment in the Dockerfile both say this is required behind Access.
How the fix was verified
This is the part that differs from the first task. The fix was checked in the built result, in two ways.
In the build. The build step that patches index.html ends with a grep for the manifest link in the patched file. If the edit does not land, for example because a future Streamlit version changes its index.html, the grep fails and so does the build. The commit message describes this as “grep-verified in the build”.
Over HTTP. Against the running app, the commit records that the head links were present and that the manifest returned status 200 with the content type application/manifest+json. That last check matters behind Access.
The test suite stayed at 120 passing tests.
What is still open
The task log records caveats instead of claiming full success. It lists two:
- Some iOS versions fetch the apple-touch-icon without cookies. If the icon still does not appear on an iPhone, an Access bypass policy covering only the static icon and manifest files may be needed.
- Phones cache home-screen icons. The old shortcut has to be removed and added again.
The material does not say whether the icon then appeared on Henning’s phone, so we are not claiming it did.
The rule
Verify in the built result, not just in the source.
In the first task the right files existed and the tab looked right. In the follow-up we checked the patched index.html during the build and fetched the actual manifest over HTTP. The first kind of check confirms the work was done. The second confirms it reached the user. When a feature depends on what the deployed system serves, the check belongs where it is served.