# Test report: Home Screen web app behaviour on iPhone 16 Pro, iOS 18.6.2 (22G100) - Report id: OPWA-DT-2026-10-20-ios18.6.2 - Citation string: `OpenPWA device test, 2026-10-20, iPhone 16 Pro, iOS 18.6.2 (22G100)` - Run date: 2026-10-20, 14:36 to 17:35 CST (06:36 to 09:35 UTC) - Report written: 2026-10-20, from the lab notebook as archived in this folder (entries N01 to N63) - Authorship: the device steps were performed by an OpenPWA operator on a real device. Instrumentation, the lab notebook and this report were produced by AI agent sessions of the OpenPWA Editorial Team; no person has signed off this report entry by entry. Byline for site use: Carey Gill (editorial pen name). - Status: final for the iOS 18.6.2 baseline, with the open items listed in section 12. Companion documents in this folder: `PAPER.md` (research article), `PAPER.zh.md` (Chinese version), `FINDINGS.md` (headline findings), `SITE-UPDATES.md` (proposed site edits). The primary record is `2026-10-ios18-baseline.md` (the lab notebook, "the notebook" below). Where this report and the notebook disagree, the notebook's latest entry on the topic wins, except for the corrections this report adds in section 9.3, which are new and should be appended to the notebook. --- ## 1. Abstract One operator ran the OpenPWA device-test protocol on an iPhone 16 Pro with iOS 18.6.2 (22G100) and Safari 18.6, while an agent read page state over USB through the WebKit remote inspector protocol after each step. Eleven tests (T1 to T11, plus T4b) covered how Home Screen items open, which icon is used, storage and cookie separation between Safari and the Home Screen app, the status-bar area, notification permission, push subscription, `notificationclick`, Badging, the absence of install APIs and the exposure of Declarative Web Push. Main results: a manifest-less, icon-less page added to the Home Screen opens in Safari with Safari's storage; a manifest page opens standalone in a 402 x 812 viewport; localStorage and IndexedDB are not copied into the app and stay separate, while a first-party cookie was copied from Safari into the app once, consistent with copy at Add to Home Screen (as WebKit documents for 17.2), and was independent afterwards; `notificationclick` fired through both `addEventListener` and the `onnotificationclick` property on 6 of 6 taps in the installed app, and `'onnotificationclick' in self` was true in the app's worker but false in a tab-controlled worker, which explains the BCD "unsupported" entry; the permission prompt appeared after a tap and `requestPermission()` stayed pending for 2068 ms; with the default status-bar style the strip above the viewport was a solid #161616 and content never reached it, while `black-translucent` with `viewport-fit=cover` put the page under the status bar (viewport 402 x 874, top inset 62 px); `setAppBadge(3)` showed "3" on the icon. A follow-up manifest-vs-meta test (T4 18c, N64 to N66) showed that the strip takes the page's `theme-color` meta when present (over the manifest `theme_color`, also inside an existing app) and falls back to the manifest `theme_color` without one. ## 2. Scope In scope: one device, one OS build, Safari and Home Screen web apps added from Safari, the openpwa.net demo and device lab pages listed in section 5.1, and the questions of `2026-10-ios27.md` T1 to T11 run as a pre-iOS 26 baseline. Out of scope or not run: - Real push delivery (no push server; the lab's VAPID private key was discarded). Only local notifications from `registration.showNotification()` were tested. - The `clients.openWindow()` path of `notificationclick` (could not be reached, section 7.8). - T5b (third-party browser Add to Home Screen): not run. - T1 steps 4 to 6 (same-origin and cross-origin navigation from the manifest-less icon, relaunch URL): not run. - T4 threshold page (T4_7 to T4_10): not run (its precondition, a visible difference between baseline and fixed, did not occur). - T4b `sbs-default` and `sbs-black`: never installed (N50-a, N59). - iPadOS, other iPhone models, other iOS builds; Dark appearance (operator report only). - Time budget: the protocol caps a checklist at 60 minutes (`PROTOCOL.md`); this run took about three hours, mostly because of added discriminating steps and tool outages. Recorded as a protocol deviation. ## 3. Environment | Item | Value | Source | |---|---|---| | Device | Apple iPhone 16 Pro, ProductType iPhone17,1, HardwareModel D93AP, order number MYLU3CH/A (mainland China hardware) | `ideviceinfo` (notebook Environment) | | Model number (A-number) | not read | pending (section 12) | | OS | iOS 18.6.2, build 22G100, release channel | `ideviceinfo`; safaridriver capabilities `safari:platformVersion` 18.6.2, `safari:platformBuildVersion` 22G100 | | Safari | 18.6 (safaridriver `browserVersion` "18.6") | N03 | | User agent | `Mozilla/5.0 (iPhone; CPU iPhone OS 18_6_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Mobile/15E148 Safari/604.1` | N03, `raw/2026-10-20-ios18.6.2-tab-t1-no-manifest.json` | | Language | `navigator.language` "zh-CN", `navigator.languages` ["zh-CN"]; system alerts in Chinese (N58) | probe reads | | Time zone | Asia/Hong_Kong (UTC+8) | `ideviceinfo` | | Appearance | Light (operator, Settings, ~17:19 CST); `prefers-color-scheme: dark` false in every agent read 06:54 to 09:31Z. Switched to Dark only for the N48/N49 check, which has no agent read in Dark | N47-a, N48 | | Safari settings | Web Inspector on, Remote Automation on (set by the operator before the run) | Methods | | Safari Web Extension | at least one installed (`safari-web-extension://…` background page listed); identity unknown | N04, A2 | | Feature flags, VPN, iCloud Private Relay | not reported | pending (section 12) | | Device state | not wiped; other sites' Home Screen apps present; an earlier manifest-less icon from 14:45 CST present | Threats to validity | | Host | MacBook Pro, macOS 26.3 (25D125), Safari 26.3, USB, paired and trusted | Methods | | WebDriver | `safaridriver` "Included with Safari 26.3 (21623.2.7.11.6)", capabilities `{"browserName":"safari","platformName":"iOS","safari:useSimulator":false}` | N03 | | Inspector bridge | `ios_webkit_debug_proxy` 1.9.2 (Homebrew; libimobiledevice 1.4.0, libplist 2.8.0, libusbmuxd 2.1.1) | N04, Methods | | Device info | `ideviceinfo`, `idevicepair` (libimobiledevice 1.4.0) | Methods | | Runtime | Node.js 26.0.0 | Methods | | Recording and image analysis | ffmpeg (Homebrew), Python PIL for pixel sampling and EXIF times | N57, N59 | Tool hashes (SHA-256, from `tools/SHA256SUMS`, as recorded during the run): | Tool | Role | SHA-256 | |---|---|---| | `probe.mjs` | WebDriver driver, tab reads (N03) | `b594c0a2acae8cd9a3191edbcb470aaca61d7a8c3136637522ba8b19ba5f345a` | | `probe-t1.mjs` | WebDriver driver, T1 probe page (N06) | `62ab60950de2a924dbaddd837a616f1cf59173ebe624e17035e3d48d5b8ac086` | | `probe-wi.js` | inspector probe expression | `30dcbe4b6948a39ba4ff9ba3278f6d04ef87216e9f55fb7a03092957a60b6be4` | | `wi-eval.mjs` | inspector client (v3, waits for the target) | `fb4d9b08de1f204faeedb563386ba1e4fcdb8aa6c730b023180dc3f4f72b3843` | | `wi-read.sh` | read wrapper and sidecar writer | `155daa9927dce638002bceaafb663d276abac92fd664fbe899350af2f8d4956a` (the archived copy has the device UDID replaced by a `UDID` variable and no longer matches; see README) | | `marker.js` | T6 marker reader | `a60b1f789978613133445c24949cdc02a47f0143c302b48f316700fa39569fe0` | | `timing.js` | document timing | `d7b1fa8512112c8de0231dbb5010a900075d121a8455c5e322ddad4de6c800b8` | | `t4.js` | T4 geometry read | `6af7a07ac8f0b2eb75e7e8f01df838a1b79e41444ec4408fce514ca30a4ca48e` | | `t4b.js` | T4 geometry and colour read | `69d66f9c30bf38597678c35d58f4dd2ff8e25c52bef67a3fed716a4849a5bcb8` | | `perm.js` | permission state read (requests nothing) | `f536df7f8196a4a7d0f6d2af2669035a264704bcc0542008bc172e9da04931e2` | | `perm7.js` | Perm Test state read (requests nothing) | `99fece34001284dd9424bd61ba017f07f604ac79ce8436707233e088daa9fd91` | | `sub.js` | subscription read, endpoint host only | `9a1d7f87e56a4f3dbbdfcb5005420dfde7a55a0ea838055b05c7736d3078d9f2` | | `exposure.js` | T8 worker exposure read | `a31d585078ce3d933a4a5cc765f191c6630b9908da8091f29de5c7d03f39a14c` | | `events.js` | T8 event records read | `be959681ea3d06d4539eada6a6b3162466f74b409d5797b1961ff1b472c3bceb` | | `events2.js` | `events.js` plus `lab.js` version check | `cd0fddeddb63519e0b173c1889d7690e0b9c78136796e41593a56cd39afab761` | | `demo-push.js` | demo push read | `b1c2dbad5d8d3a8ff5dc904d3a9278e975efc0e2695c43652f46738f8a86035c` | | `badge.js` | badge read (never run, N43) | `3b4b63c12bfaf4ff2790afdf9104215b6e59ff64f032d56389fa6f7ffa162b3a` | ## 4. Personnel and roles - **Operator**: an OpenPWA operator, owner of the device, performed every tap and Settings change, took the screenshots and the screen recording, and answered questions in chat. - **Instrumenting agent**: an AI agent session on the Mac that ran the reads, kept the notebook, and analysed the screenshots and the recording (N57 to N63). - **Coordinator**: a second AI agent session that relayed the tap list, questions and answers between the operator and the instrumenting agent. One coordinator relay was a paraphrase of the planned step rather than an operator report (N32-b); see section 9. ## 5. Method ### 5.1 Harness pages All pages are on `https://openpwa.net/`, served from the repository's `site/public/`. The lab is `noindex` and not linked from the site. | Page | Live URL | Role | Source state | |---|---|---|---| | Demo | `/demo/`, `/demo/push/`, `/demo/badging/` | T2, T5, T6, T7, T9, T10, T11 | unchanged during the run (e.g. `demo/push/index.html` sha256 `b8b427c91b67c2cb…` at 750a5d12 and df421952) | | Lab | `/lab/device-tests/` | T3, T5, T6, T7, T8, T9, T10 | `lab.js` `849c0ac7a21d01ff…` served N03 to N36 and, from the HTTP cache (`max-age=14400`), also after the 1124b20d deploy (N37, N38-a); live `lab.js` from 08:28Z `eb7c4d5ddfb2986a…`; `sw.js` `0e7e6f34cb814d80…` (`lab-sw-v2`) unchanged throughout | | Lab no-ATI | `/lab/device-tests/t3/manifest-only/` | T3, T6 cookie timing | manifest icons only, no service worker | | T1 probe | `/lab/device-tests/t1/no-manifest/` | T1 | no manifest, no icon, no theme-color, no SW; `index.html` `79d08968039e334e…`, `probe.js` `c274df3c9ee717ee…` (commit 77080a59) | | T4 variants | `/lab/device-tests/t4/{baseline,fixed,sticky,safe-area,threshold}/` | T4 | present at 750a5d12; all link the lab manifest; theme-color `#161616` | | T4 strip set | `/lab/device-tests/t4/{white-top,theme-red,bg-green,dynamic}/` | T4 | added by 1124b20d (live ~08:28Z); theme-red meta `#d00000`, the others `#161616`; all link the lab manifest (`theme_color` `#161616`) | | T4b apps | `/lab/device-tests/t4/sbs-{default,black,black-translucent}/` | T4b | each its own manifest (`theme_color` `#161616`); only `sbs-black-translucent` was installed | | Perm Test | `/lab/device-tests/t7/perm/` | T7 step 25c | added by df421952 (deploy 39397275280); live snapshots in `tools/site-snapshots/` (`t7-perm-index.html` `b7240ef04a150…`, manifest `e667b5f34c2c4…`, `sw.js` `6a22bfccf2562…`) | | Manifest-less reference page | `/reference/installation/ios-add-to-home-screen/` | T1 (14:45 CST run) | no manifest, no apple-touch-icon | Every manifest in the harness declares `theme_color` `#161616` (lab, t3, sbs-*, Perm Test, demo). This matters for T4 (section 7.4). Snapshot note: the live `t7/perm/index.html` snapshot differs from the git blob at df421952 in one CSS declaration (`font-weight:600` vs `700` on the header). No behavioural difference; the snapshot is the record of what was served. ### 5.2 Collection methods 1. **Automated tab (WebDriver).** `safaridriver` opens an automation window on the phone; no taps. Used only for tab-side values (N03, N06). Each session has its own empty website-data store (A1), so no storage result comes from this path. 2. **Inspector reads.** After each operator step the agent ran `wi-read.sh`, which restarts `ios_webkit_debug_proxy`, selects the foreground page by URL (and, from N14 on, by process id), runs a read-only expression with `wi-eval.mjs`, and saves `raw/.json` plus (from N15) a `.meta.json` sidecar with UTC time, tool hashes, the selected page and the redacted page list. Home Screen apps appear as a separate process (PID:24500) from Safari (PID:24364). Pages are readable only while in the foreground (A5). 3. **Operator reports.** Short chat answers, quoted verbatim in the notebook and marked as owner reports. 4. **Recorded evidence.** Screenshots and one screen recording, captured by the operator and analysed by the agent (EXIF capture times, pixel sampling, frame extraction; N57 to N63). The probes never call `requestPermission`, `subscribe`, `share`, `install`, `register` or `persist`, and record push endpoints as host only. State changes come only from the operator's taps on page buttons. ### 5.3 Expectations and verdict rules Expectations were written before the run in `2026-10-ios27.md` (per test, from vendor documents, BCD and our own pages) or, for added steps, in the notebook before the read (for example the H1/H2 cookie hypotheses in N18 and the copy-at-add prediction in N21). Verdicts: `as-expected`, `differs`, `inconclusive` (the step cannot discriminate), `could-not-run`. Corrections are appended as new entries; nothing is edited in place. ### 5.4 Confidence labels used in this report - **A**: agent-measured (an instrumented read in `raw/`). - **S**: recorded (operator-captured screenshot or recording, analysed by the agent). - **O**: operator verbal report only. - Combinations (A+S, A+O) mean independent sources agree. - **R**: retracted; kept for the record, not to be cited. ## 6. Results summary | Test | Result | Confidence | Notebook | |---|---|---|---| | T0 | frozen UA `Version/18.6`, `iPhone OS 18_6_2` | A | N03 | | T1 | manifest-less, icon-less items open in Safari (bookmark): standalone false, display-mode browser, Safari storage and SW | A+S (O for the 14:45 add sheet) | N02, N06, N23, N63 | | T2 | manifest demo opens standalone; display-mode standalone only | A+S | N13, N57 | | T3 | 180 apple-touch-icon used when present; manifest `any` 192 when not; letter tiles for icon-less pages | S (+O) | N11, N12, N63 | | T4 | default style: solid strip, viewport starts 62 px below the top, content never under it, no blur; strip colour = page `theme-color` meta if present, else manifest `theme_color` (#161616 on the harness pages) | A (geometry) + S (pixels) + O | N25 to N29, N62, N64 to N65-a | | T4b | `black-translucent` + `viewport-fit=cover`: viewport 402 x 874, top inset 62 px, page drawn under white status-bar text | A+O | N55 | | T5 | tab: browser, `standalone` false; app: standalone, true | A | N03, N13 | | T6 | localStorage and IndexedDB not copied, isolated both ways; cookie copied once Safari to app (consistent with copy at add, moderate), then independent; separate cookie store per app | A | N13, N16, N17, N18 to N22 | | T7 | tab: no `Notification`, no `PushManager`; app: prompt after tap, granted, `web.push.apple.com`, listed in Settings; `requestPermission()` pending 2068 ms on a clean app | A+S | N10, N32, N33, N41, N54, N58, N59, N61 | | T8 | `notificationclick` fires via both paths, 6/6 taps; existing window focused every time; `openWindow` not reached | A (+S for banners) | N18, N34 to N40, N63 | | T9 | no `navigator.install`, no `BeforeInstallPromptEvent`, tab and app | A | N03, N13 | | T10 | `window.pushManager` in tab and app; tab lacks `PushManager` and `Notification`; window and SW subscriptions separate | A | N03, N13, N33, N41 | | T11 | `setAppBadge(3)` shows "3" on the icon; clearing: operator report only | S (+O) | N60 | ## 7. Results per test Each subsection gives expected, observed, evidence pointers and confidence. Times are UTC unless marked CST. ### 7.1 T1: manifest-less page added to the Home Screen Expected (pre-26 control): opens as a Safari bookmark; no "Open as Web App" switch in the sheet (the switch arrived with iOS 26, WebKit's Safari 26.0 post). Observed: - 14:45 CST, `/reference/installation/ios-add-to-home-screen/` (no manifest, no apple-touch-icon): the add sheet showed a title field and no switch (O); icon a black letter tile "I", label "iOSAddtoHom…" (O, and visible in T8_1b and the 25c Home Screen frame, S). Opened from the icon, the Web Inspector console gave `[false, false, true, false, false, false, "https://openpwa.net/reference/installation/ios-add-to-home-screen/"]` for `[navigator.standalone, standalone, browser, minimal-ui, fullscreen, application, href]` (O: values copied by the operator from the console; the snippet as run has seven entries, see notebook T1-WI). - T1 probe page in a tab (WebDriver, 07:08:43Z): standalone false, browser only, no registrations (A, `raw/2026-10-20-ios18.6.2-tab-t1-no-manifest.json`). - T1 probe from its own icon (07:49:47Z): the page loaded in Safari's process (PID:24364), nothing for it in the web-app process; inner 402 x 678 (the tab size); standalone false; display-mode browser; Safari's localStorage (`openpwa-lab-marker` `TAB-2026-10-20T07:45:25Z`, `openpwa-demo-lang` `en`) and Safari's lab service worker controlling the page (A, `raw/2026-10-20-ios18.6.2-step14-t1probe.json`). Screenshot T1_4 shows Safari's address bar and toolbar (S, N63). Verdict: as-expected. Confidence A+S for the T1 probe. The sheet's lack of a switch is O only (the add-sheet screenshots T1_0, T3_0 and T2_1 were not delivered). ### 7.2 T2 and T5: display mode, tab vs installed app Expected: tab `browser`; app `standalone`, `navigator.standalone` true. Observed: in the tab (WebDriver on four pages; normal profile in N08 to N10) `navigator.standalone` false, display-mode `browser` true and the other six queries false; inner 402 x 678. Demo app (07:21:34Z): standalone true; display-mode standalone only (browser and application false); page line "Running as an installed app: display-mode: standalone (iOS navigator.standalone)"; inner and visualViewport 402 x 812, screen 402 x 874, insets 0 (A, `raw/2026-10-20-ios18.6.2-step09-app-demo.json`; S, T2_2). Verdict: as-expected. Confidence A+S. ### 7.3 T3: Home Screen icons Expected: PWA Lab (apple-touch-icon plus manifest icons) uses the 180 apple-touch-icon; Lab no-ATI (manifest icons only) uses 192 or 512; icon-less pages get a letter tile or a screenshot. Observed: PWA Lab red "180 apple-touch"; Lab no-ATI blue "192 manifest any"; T1 probe black "T" (label "T1no-manifest…"); manifest-less reference page black "I"; demo its own artwork; SBS transl red 180 (the sbs pages link the lab's `ati-180.png`); Perm Test yellow "P7" (O at N11, N12, N52; S in T8_1b, T11_1 and the 25c Home Screen frame, N63). The T8 notification banners also carry the 180 icon (S, T8_1a). Verdict: as-expected. Confidence S. The add sheet (prefilled name, any switch) is not evidenced (O only for T1; nothing for T2 and T3). Update (N66, S+O): the add sheet for the T4 18c M1 app (a page with a manifest) was screenshotted (`screenshots/…_T4_18c_sheet_redacted.png`): title 添加到主屏幕, 取消 / 添加, the apple-touch-icon as the tile, an editable name field prefilled "Theme M1" (equal to both `apple-mobile-web-app-title` and manifest `short_name`), the truncated URL, the footnote 将在主屏幕添加图标,以便你快速访问此网站。, and **no "Open as Web App" switch**. The operator reports the same ("添加界面没有开关"). T2 and T3 sheets themselves remain unevidenced. ### 7.4 T4: status-bar area with the default status-bar style Expected (pre-26): no blur; content below the status bar for the default style (Apple's Supported Meta Tags). Observed, geometry (A): in the PWA Lab app the baseline, fixed, sticky and safe-area variants all had inner height 812 of an 874 px screen and `safe-area-inset-top` 0. With `viewport-fit=cover` (safe-area variant) the bottom inset became 34 px, but the top inset stayed 0 and the viewport still started 62 px below the top (N25, N26, N28, N29; `raw/2026-10-20-ios18.6.2-step15r-t4-baseline.json`, `raw/2026-10-20-ios18.6.2-step16-app-t4-{fixed,sticky,safe-area}.json`). White-top (09:05:59Z): colour at the viewport top white, theme-color `#161616` (A, N45-b). Observed, colour (S): in every portrait screenshot of a default-style page the strip is rgb(22, 22, 22) = `#161616` at all sample points, never `#000000`: in the app T2_2, T4_2, T4_3a, T4_3b, T4_4, T7_sub_1, T8_1a, T4_reobs_1 to 7 (two passes, scroll 0), and `unassigned_1` (sampled for this report; not in N62's list); in Safari tabs T4_5 (scrolled, white page top) and T1_4 (N62). T7_2 shows rgb(17, 17, 17) because the system alert dims the screen. The operator's verbal "solid black, no content and no blur behind the time" (N47, N47-b, O) agrees with the pixels in substance. Not settled: which declaration produces `#161616`. Every screenshotted in-app page has `theme-color` `#161616` and links a manifest whose `theme_color` is `#161616`. The only discriminating page, theme-red (meta `#d00000`, manifest `#161616`), has an operator report of "all black" (N47, O) and no screenshot. The leading hypothesis (N62) is that the in-app strip takes the manifest `theme_color`; a fixed system dark grey that happens to equal `#161616` is unlikely but not excluded. Bg-green, white-top and dynamic are O only. Dark appearance: operator report "all black" (N48, N49, O); the one agent read taken then found `prefers-color-scheme: dark` false, so Dark is unconfirmed. Safari tab: T4_5 (header scrolled away, white page top) shows `#161616`, the page's theme-color, so the tab tints by theme-color, not by the content at the top (S, N62). The T1 probe page declares no theme-color; its `#161616` in T1_4 matches the page's own dark header (`probe.css` background `#161616`), not a theme-color (correction C2, section 9.3). Landscape (app, 08:12:01Z, A): orientation `landscape-primary`, inner 750 x 402 of 874 x 402, insets 0 (no `viewport-fit=cover`). Whether the app shows a status bar in landscape and the colour of the side bands is uncertain: N31-a and N31-b belong to the retracted series, and the landscape screenshot shows a Safari tab, not the app (N63). Verdict: no blur, strip solid, content never under it (A+S). Strip colour `#161616` (S). Independence from the page's `theme-color` meta: unresolved (O only for the discriminating page). Update, T4 18c (N64 to N65-a; S+O, no agent read): three separate apps and the theme-red page settle the source. Strip pixels (six points inside the 186 px strip, identical at every point): **M1** (manifest `theme_color` `#d00000`, meta `#00a050`) `#00a050`; **M2** (no manifest colour, meta `#d00000`) `#d00000`; **M3** (manifest `#00a050`, no meta) `#00a050`; **theme-red** in the PWA Lab app (meta `#d00000`, lab manifest `#161616`, `T4_12a`) `#d00000`. The operator's report ("M1绿、M2红、M3绿。theme-red 红") agrees. So the in-app strip takes the **page's `theme-color` meta** when present, over the manifest, and from the current page inside an existing app; the manifest `theme_color` is used only when the page has no meta. N62's manifest hypothesis and the "fixed dark grey" alternative are refuted, and the earlier "all black" for theme-red (N47, O) is superseded (N65-a). Not tested: Dark appearance, `media`-qualified metas, a meta changed after load. ### 7.5 T4b: `apple-mobile-web-app-status-bar-style` Expected (historical, Apple's Supported Meta Tags): with `black-translucent` "the web content is displayed on the entire screen, partially obscured by the status bar"; with `default` or `black` it is displayed below the status bar. Observed: SBS transl app (09:31:04Z): meta `black-translucent`, viewport `width=device-width, initial-scale=1, viewport-fit=cover`; inner 402 x 874 (the full screen); insets top 62, right 0, bottom 34, left 0; static blue header at top 0; colour at the viewport top rgb(15, 98, 254) (A, `raw/2026-10-20-ios18.6.2-N55-app-sbs-transl.json`). The operator saw the blue header behind the time, with white status-bar text (O, N51, N55). No screenshot (T4b_1 to 3 missing). `sbs-default` and `sbs-black` were never installed (N50-a; the 25c Home Screen frame shows SBS transl only, N59). Anomaly A11 (found while writing this report, section 9.3): in the same N55 read the page's own probe block (`variantProbe`, generated by `lab.js` earlier in the page's life) printed `safe-area-inset-top: 0px`, while `t4b.js` computed 62 px at read time with the same probe-element method. `innerHeight` 874 supports 62 px. Probable cause: the on-page block was computed before the insets applied at launch; not verified. Verdict: as-expected (historical behaviour). Confidence A+O. ### 7.6 T6: storage and cookies, Safari vs Home Screen app Expected (checklist, from WebKit's tracking-prevention page "kept isolated from Safari"): nothing copied, no sharing. The checklist did not consider WebKit's Safari 17.2 post, which documents that cookies are copied when a web app is created and that no other storage is (correction C1, section 9.3). Observed, demo language marker (localStorage): the Safari tab set `zh` (07:12:14Z, N08); the app's first launch showed English with empty localStorage and no cookies (07:21:34Z, N13, A); the tab switched to English and the relaunched app still read `zh` (N16; A for the app, O for the tab); the app wrote `zh` last while the tab held `en`, and the tab read `en` without a reload (N17; A for the tab, O for the app's write). Observed, lab marker (localStorage, IndexedDB, and cookie `openpwa_lab_marker` with `path=/lab/device-tests/; max-age=31536000; SameSite=Lax; Secure`, set by `document.cookie`): | Step | UTC | Context | localStorage | IndexedDB | cookie | Notebook | |---|---|---|---|---|---|---| | Safari writes TAB | 07:13:46 | tab | TAB-…07:13:46Z | TAB-…07:13:46Z | TAB-…07:13:46Z | N09 | | PWA Lab and Lab no-ATI added | ~07:16 | | | | | N11 | | PWA Lab first launch | 07:34:15 | app | null | null | **TAB-…07:13:46Z** | N18 | | PWA Lab writes APP | 07:37:40 | app | APP-…07:37:40Z | APP-…07:37:40Z | APP-…07:37:40Z | N20 | | Safari reload | 07:38:18 | tab | TAB-…07:13:46Z | TAB-…07:13:46Z | TAB-…07:13:46Z | N19 | | Safari writes a new TAB | 07:45:25 | tab | TAB-…07:45:25Z | TAB-…07:45:25Z | TAB-…07:45:25Z | N22 | | Lab no-ATI first launch | 07:45:32.575 | app | null | null | **TAB-…07:13:46Z** | N21 | Verdict: localStorage and IndexedDB were not copied and stayed isolated in both directions (A). The cookie was copied from Safari into each app once and was independent afterwards; each Home Screen app had its own cookie store (Lab no-ATI did not see PWA Lab's APP cookie although both share the path and the process) (A). Copy time: Lab no-ATI received the value Safari held when it was added, not the value written 7 s before its first launch, which is consistent with copy at Add to Home Screen (A, strength moderate: with a 7 s gap a first-launch copy from a not-yet-flushed Safari cookie store cannot be excluded; N22 proposes a stronger retest). This agrees with WebKit's Safari 17.2 statement (section 8, D5 and D6). The notebook's "differs (cookie)" verdict is a difference from our incomplete expectation, not from WebKit's documentation. Not tested: cookies with other attributes (HttpOnly, `SameSite=None`, session cookies, partitioned cookies), server-set cookies, CacheStorage, OPFS. ### 7.7 T7: notification permission and push subscription Expected: no `Notification` interface in a tab; in the app a prompt follows a tap, the result is `granted`, the subscription succeeds with an Apple host, and the app is listed in Settings. Observed: - Tab (N03, N10, A): `'Notification' in window` false, `'PushManager' in window` false, `window.pushManager` present. `/demo/push/` reported four unmet prerequisites, including "Safari 标签页中不暴露 Push API;请先添加到主屏幕。" (the zh UI string for "In Safari tabs the Push API is not exposed; add to Home Screen first."). - PWA Lab app (step 19): `default` at 07:34:15Z (N18); `granted` at 08:18:29Z with both `permissionState` values (registration and `window.pushManager`) granted (N32, A). A system prompt is visible in T7_2 at 08:17:36Z (S, N58), verbatim "“PWA Lab”想给你发送通知 / 通知可能包括提醒、声音和图标标记。可在“设置”中配置相关功能。 / 不允许 / 允许" (gloss: "PWA Lab" would like to send you notifications. Notifications may include alerts, sounds and icon badges. These can be configured in Settings. Don't Allow / Allow). - Subscription (08:20:33Z): host `web.push.apple.com`, endpoint path length 172, `expirationTime` null, `userVisibleOnly` true, `applicationServerKey` equal to the lab key; `window.pushManager.getSubscription()` null (N33, A; T7_sub_1, S). - Demo app (step 25, 08:49:29Z): the operator reported a prompt; `granted`; host `web.push.apple.com`; `window.pushManager.getSubscription()` null (N41, A+O). The pre-tap read and recording planned for this step were skipped; the pre-tap state is the N13 read (`default`, 88 min earlier). - Perm Test, a never-prompted app (step 25c): before the tap, `default` and both `permissionState` `prompt` (09:27:35Z, N53, A); page log `startMs` 1792488479551 (09:27:59.551Z), `endMs` 1792488481620 (09:28:01.620Z), elapsed **2068 ms**, result `granted` (N54, A). The recording shows the prompt from t of about 70.6 s to 72.5 s of the original file, verbatim "“Perm Test”想给你发送通知 / …", aligned with `startMs` and `endMs` within the 1 s resolution of the file's `creation_time` (N59, S; key frames in `recording/`). - Settings (S, N61): Settings → 通知 lists "OpenPWA 横幅、声音、标记" and "PWA Lab 横幅、声音、标记"; the OpenPWA detail page has 允许通知 on and 标记 (Badges) on (T7_4a, N60). Verdict: as-expected. One grant covered both the service-worker `PushManager` and `window.pushManager` (A). Anomaly A7 (no prompt remembered at step 19) is closed: the prompt was shown, and it is in the operator's own screenshot (N58). ### 7.8 T8: `notificationclick` in the installed app Expected (checklist hypotheses): no record (the handler does not fire, WebKit bug 268797); a listener-only record (the property path is broken, consistent with BCD); or both records. Worker exposure (A): in a worker controlled by a Safari tab (N03), `'onnotificationclick' in self`, `'onnotificationclose' in self`, `'onpush' in self`, `'onpushsubscriptionchange' in self`, `'Notification' in self`, `'showNotification' in registration` and `'pushManager' in registration` were all false. In the same worker script (`lab-sw-v2`) controlled by the Home Screen app, all were true and `typeof self.onnotificationclick` was "object", captured at worker start before the script assigns the property (N18 before permission, N34 after). `typeof clients.openWindow` was "function" in both contexts. Taps (A, from the worker's IndexedDB records). The lab worker installs a handler with `addEventListener` and another through `self.onnotificationclick`; neither calls `event.preventDefault()`. Only the listener focuses or opens a window, after `clients.matchAll({type: "window", includeUncontrolled: true})`. | Tap | Condition | Notification created | Listener record | Property record | Navigation (listener) | Notebook | |---|---|---|---|---|---|---| | 1 | 2b, banner, Home Screen | 08:22:57.000 | 08:23:07.007 | 08:23:07.008 | windowsFound 1, focused existing window | N35 | | 2 | 2b, banner over the app in front (T8_1a) | 08:23:21.253 | 08:23:30.533 | 08:23:30.534 | focused existing window | N35, N63 | | 3 | 2b, banner over the Home Screen (T8_1b) | 08:24:38.479 | 08:24:46.071 | 08:24:46.072 | focused existing window | N35, N63 | | 4 | 2a, tapped in Notification Centre | 08:26:28.601 | 08:26:49.588 | 08:26:49.588 | focused existing window | N36 | | 5 | after swipe-away and relaunch, 2b | 08:28:40.822 | 08:28:50.048 | 08:28:50.061 | focused existing window | N38 | | 6 | 2b, app swiped away before the tap | 08:43:03.022 | 08:43:09.236 | 08:43:09.245 | windowsFound 1, focused existing window | N40 | Twelve records, two per tap; no `notificationclose` record. The interval from creation to record includes the 5 s timer and the human's reaction and is not a latency. Other observations: a banner was shown while the app was frontmost (N36-a, O; T8_1a, S). After tap 6 the document from the relaunch (N38) was still alive about 6 s after the swipe-away and `clients.matchAll` found it (N40, A); the web-app process id also survived a swipe in N16. Verdict: both handler paths fire, 6/6 (A). `focus()` on an existing window succeeds (A). The `openWindow` path (no window alive) is **could-not-run** with this method, because swiping the app away did not end the page within seconds. ### 7.9 T9: install APIs Observed (A; tab N03, app N13): `'install' in navigator` false, `typeof navigator.install` "undefined", `'BeforeInstallPromptEvent' in window` false, `'onbeforeinstallprompt' in window` false, `'getInstalledRelatedApps' in navigator` false; `'onappinstalled' in window` false in the tab. Verdict: as-expected. ### 7.10 T10: Declarative Web Push exposure and the two subscription stores Observed (A): `'pushManager' in window` true in the tab and in the app, with `subscribe`, `permissionState` and `getSubscription` functions; in the tab `PushManager` and `Notification` are absent (N03, N10). In the lab app and the demo app, after a successful service-worker subscription, `window.pushManager.getSubscription()` returned null (N33, N41). One permission grant set both `permissionState` values to granted (N32, N54). Not tested: subscribing through `window.pushManager`, in a tab or in an app. Verdict: as-expected for exposure (BCD `api.Window.pushManager`, Safari iOS 18.4). The separation of the two subscription stores is an observation with no prior expectation. ### 7.11 T11: Badging Expected: a badge on the Home Screen icon once notification permission is granted (WebKit's Safari 16.4 post: Badging permission is granted together with notification permission). Observed: the demo app held notification permission from 08:49Z (N41). After three "Increment and set badge" taps, T11_1 (08:54:41Z) shows a red "3" on the OpenPWA icon (S, N60). No agent read (inspector listing empty, A8). Clearing: operator report only (N44-a, O). The conflicting "no Badge" (N56) fits the state after Clear. Verdict: as-expected. Confidence S for the badge, O for clearing. ## 8. Discrepancies with external sources and with our own pages | # | Source | Source says | Observed here | Assessment | Proposed action | |---|---|---|---|---|---| | D1 | BCD `api.ServiceWorkerGlobalScope.notificationclick_event`: `safari_ios` `version_added: false` (raw `api/ServiceWorkerGlobalScope.json`, read 2026-10-20). Issue mdn/browser-compat-data#22959 (open): a BCD contributor states the collector checks `"onnotificationclick" in self` and that the check fails on Safari iOS 16, 17 and 18 | unsupported | in a Home Screen app worker the check is true and the event fires via both paths (6/6); in a tab-controlled worker the check is false | BCD's entry reflects a context in which the event cannot occur (there is no `Notification` in a tab); support is context-dependent | Evidence comment on #22959 (parked until accounts are approved; human-written per the authority plan) | | D2 | Apple, Supported Meta Tags (archived, updated 2014-07-15): "If content is set to default, the status bar appears normal … The default value is default." | "normal" (no colour defined) | default style: solid `#161616` strip, content below it | Not a contradiction of Apple's text, which defines no colour; it contradicts readings of `default` as a light bar (our own checklist T4b expected "default = light status bar") | Do not describe `default` as light on our pages; state the observed colour and its unresolved source | | D3 | firt.dev "iOS PWA Compatibility" (updated 2026-10-13): `apple-mobile-web-app-status-bar-style` "👎 since 15.0", replacement "`theme-color` meta tag"; `black-translucent` "Still the only way to get a fullscreen app" | theme-color replaces the meta tag | `black-translucent` still changes the geometry (A); the strip matches the theme colour in every screenshot, but whether the page's `theme-color` meta (as opposed to the manifest `theme_color`) controls it in standalone is unresolved, and one operator report is against it | Partly consistent; the "replacement" claim is not confirmed for the meta tag in standalone | Keep `black-translucent` documented as effective on 18.6.2; run the manifest-vs-meta test | | D4 | Our demo `/demo/push/` string "In Safari tabs the Push API is not exposed; add to Home Screen first." | Push API absent in tabs | `PushManager` and `Notification` absent, `window.pushManager` present in the tab | Imprecise | Reword to name `PushManager` and `Notification` (SITE-UPDATES U6) | | D5 | Our page `/reference/storage/home-screen-storage-isolation/`, caution: "a sign-in flow whose state is a token written during the Safari session starts in the installed app without that token" | nothing carried over | cookies are copied once at creation (WebKit 17.2; observed); localStorage and IndexedDB are not | Wrong for cookie-held tokens; omits a primary source | Correct with WebKit 17.2 and this test (SITE-UPDATES U1) | | D6 | WebKit, Tracking Prevention: the website data of Home Screen web apps "is kept isolated from Safari" | isolated | isolated except a one-time cookie copy | Consistent with WebKit's own 17.2 post; the tracking-prevention sentence is incomplete on its own | Cite both sources together | | D7 | WebKit, New WebKit Features in Safari 15: theme-color changes "the color of the status bar and overscroll area in Safari on iOS 15" | Safari's status bar | in a Safari tab the strip takes the page's theme-color even with a white page top (T4_5) | Consistent | Optional observed note | | D8 | Modern Web Weekly #80 (2026-09-30), on iOS 27: theme-color "no longer supported since iOS26 and has no effect"; status-bar-style "deprecated and has no effect"; blur below the status bar | iOS 27 claims | 18.6.2 baseline: `black-translucent` effective; strip solid, no blur | Baseline for the iOS 27 rerun, not a contradiction | Rerun T4 and T4b on iOS 27 | | D9 | `2026-10-ios27.md` T4 step 6: "on iOS 18.6.2 the solid strip took the page's top colour, blue, not theme-color #161616, see baseline N30-a" | | N30-a was retracted (N46); the strip is `#161616` (N62) | Our own checklist repeats a retracted observation | Fix the checklist before the iOS 27 run | ## 9. Anomalies, retractions and corrections ### 9.1 Anomalies (from the notebook) - **A1** WebDriver sessions get an isolated, empty website-data store (N06); automation was not used for any state-writing step. - **A2** a Safari Web Extension is installed; identity unknown. - **A3** a page of another origin was hosted in the same web-app process (PID:24500); not read, URL not recorded. - **A4** "180 热点" read as 红 (red); the printed number is load-bearing, not the colour word. - **A5** pages are readable only in the foreground; several reads missed (N27) or hit the wrong page (N18, N19, N51). - **A6** N01 to N14 written retroactively from logs and raw files. - **A7** permission granted without a remembered prompt: **closed**, the prompt is visible in T7_2 (N58). - **A8** the inspector listing was empty for about 10 minutes (N43 to N45-a); fixed by switching Web Inspector off and on; step 27 has no agent read. - **A9** the operator took "status bar" to mean the page's blue header; colour reports retracted (N46). - **A10** an answer was given for steps that had not been done (sbs apps; N50, N50-a). ### 9.2 Retractions and withdrawn verdicts | Entry | What was said | Status | Why | |---|---|---|---| | N24 | in-scope link opened outside the app ("differs") | withdrawn (N25, N25-c) | the link was never tapped; screenshot `unassigned_1` is that attempt | | N30, N30-a | strip solid blue, matching the page's top colour, not theme-color | retracted (N46) | described the page header, not the strip (A9); pixels show `#161616` (N62) | | N31-a | Safari tab top area blue; "yes" (colour changes on scroll) | retracted (N46) | same cause; T4_5 shows `#161616` | | N31-a, N31-b | status bar shown in app landscape; side bands white | uncertain | same ambiguity; the landscape screenshot is a Safari tab (N63) | | N32 | "system prompt shown (T7_2) → Allow" | void as evidence (N32-b) | coordinator paraphrase, not an operator report; the later screenshot independently shows a prompt (N58) | | N44 | "28: yes" | clarified (N44-a) | meant "badge cleared", not optional step 28 | | N47 | strip "all black" | superseded in colour, not in scope (N62) | the colour is `#161616` | | N50 (c) | SBS default, black, transl all black | not citable (N50-a) | those apps had not been installed | | N53 | "4 entries (read-state records)" | corrected (N53-a) | only one is a read-state record | | N45-a | time 17:08 CST | corrected (N45-b) | estimate written after the fact | | N56 | "no Badge" | resolved (N60) | the state after Clear; T11_1 shows "3" | | Results summary rows T4 ("solid black … independent of theme-color"), T7 ("probable observer miss"), T11 ("uncertain") | | superseded by the 17:47 to 17:56 update rows and by this report | N58, N60, N62 | ### 9.3 Corrections introduced by this report (to append to the notebook) - **C1 (T6 framing).** The checklist expectation for T6 omitted WebKit's Safari 17.2 post, which states that on iOS 17.2 "when they tap 'Add to Home Screen' and create a web app, the cookies are copied over" and "No other kind of local storage is copied over". The notebook's "differs (cookie)" is relative to our expectation; the observation replicates WebKit's documented behaviour on 18.6.2, and the copy-at-add timing (N21, N22) is consistent with WebKit's wording. - **C2 (N62 wording).** N62 lists T1_4 among pages whose theme-color is `#161616`. The T1 probe page declares no theme-color; its strip colour matches the page's own `#161616` header. N62's conclusion is unchanged. - **A11 (new anomaly, N55).** The on-page probe text reported `safe-area-inset-top: 0px` while the agent's read reported 62 px; `innerHeight` 874 supports 62 px. Cause unverified. - **Checklist defect (D9).** `2026-10-ios27.md` T4 step 6 still cites the retracted N30-a. ## 10. Threats to validity - **Single device, single build, not wiped.** Results describe iPhone17,1 on 22G100 only. Prior Home Screen apps and Safari state existed; whether a demo icon existed before step 6 was not confirmed. - **Environment gaps.** Feature flags, VPN, iCloud Private Relay and the extension's identity are unknown (A2). The extension runs in Safari's process and could affect tab-side reads; it is not known to run in Home Screen apps. - **Observer errors.** Four kinds occurred and were caught: a step not performed (N24), a term misunderstood (A9), answers that did not match the question or the step (N44, A10), and a false memory of no prompt (A7). Each was caught by an agent read or a recording that contradicted the verbal report. Findings that still rest on verbal reports only are listed in section 11. - **Relay errors.** A coordinator paraphrase was briefly treated as an operator report (N32-b). - **Instrument effects.** Reads execute JavaScript in the page (feature detection, a hidden probe element, a temporary `window` property). They request nothing and write no storage. - **Tool outages.** The inspector listing outage (A8), a client race (N48, fixed in `wi-eval.mjs` v3) and foreground-only reads (A5) removed agent coverage for T11 and for parts of T4. - **Harness drift.** A deploy at about 08:28Z changed `lab.js`; open pages kept the cached old module (N37, N38-a). Reads after that point note which module was loaded; `sw.js` never changed. - **Pixel sampling.** The sampled `#161616` was identical at all sample points across 17 images (the 16 in N62 plus `unassigned_1`), but the screenshot pipeline's colour handling was not checked against a measured display. - **Timing resolution.** The recording's start time has 1 s resolution; the 2068 ms figure comes from `performance.now()` in the page, not from the recording. - **Irreversibility.** The device will be upgraded to iOS 27; nothing on 18.6.2 can be re-measured afterwards except on another device on the same build. ## 11. Self-review: claims that rest on operator reports or on retracted entries A skeptical-reviewer pass over this report and the companion documents. Each item appears in the text with its label. | Claim | Evidence class | Where stated | |---|---|---| | T1 add sheet has no "Open as Web App" switch | O only (no sheet screenshot); for a manifest page S+O (T4_18c_sheet, N66) | 7.1; PAPER 4.1 | | T1-WI console array at 14:45 CST | O (values copied by the operator) | 7.1 | | Strip colour independent of the page's theme-color meta (theme-red) | superseded: S+O show the strip follows the meta (N64 to N65-a) | 7.4; PAPER 4.4 | | Strip colour in Dark appearance | O only; the agent read at the time did not show Dark | 7.4 | | Bg-green, white-top, dynamic strip colours | O only | 7.4 | | Status bar shown in app landscape; side band colours | uncertain (A9 family) | 7.4 | | SBS transl: blue behind the time, white text | O (geometry is A) | 7.5 | | Demo app prompt at step 25 | O (the state change is A) | 7.7 | | Badge cleared by `clearAppBadge` | O only | 7.11 | | Banner shown while the app is frontmost | S (T8_1a) and O | 7.8 | | Demo marker: the app's `zh` in N16 and N17, the tab's `en` in N16 | O for the side not read | 7.6 | | Retracted: blue strip, page-top sampling, tab colour change on scroll | R; not used except in 9.2 | 9.2 | No headline finding in `FINDINGS.md` relies on an O-only or retracted claim unless its label says so. ## 12. Open items and pending evidence - [PENDING: add-sheet screenshots T3_0, T2_1, T1_0] prefilled name and absence of any switch (not delivered). - [DONE: T4 18c, T4_12a] manifest `theme_color` vs meta `theme-color` decided (meta wins, manifest is the fallback; N64 to N65-a). Still pending: T4_11, T4_12b, T4_13, T4_14 (bg-green, theme-red scrolled, white-top, dynamic). - [PENDING: T4b_1 to 3] no screenshot of SBS transl; sbs-default and sbs-black not installed. - [PENDING: T7_3, T8_2] demo push result and lab event log screenshots (the agent reads cover their content). - [PENDING: T4_6] the app in landscape. - [PENDING: environment] A-number, extension identity, VPN, iCloud Private Relay, feature flags, Settings language and region. - [PENDING: stronger cookie-timing retest] N22's protocol (add without launching, write a new Safari value, wait at least 2 minutes, then first launch). - Not delivered by number: IMG_8894, IMG_8905, IMG_8906 (N57). - The WebDriver page screenshots (`tab-*_1.png`) remain in the private working copy and are not in this archive. ## 13. Reproducibility 1. Device: an iPhone on the build under test; Settings → Apps → Safari → Advanced → Web Inspector on (and Remote Automation on for the tab path). Fill in the environment block of `PROTOCOL.md`. 2. Host: macOS with Safari, `brew install ios-webkit-debug-proxy libimobiledevice`, Node.js. Pair the device (`idevicepair validate`). Close any Mac Web Inspector window attached to the phone before reading. 3. Pages: use the live URLs in section 5.1. The harness source is in `site/public/demo/` and `site/public/lab/device-tests/` (commits 750a5d12, 1124b20d, df421952). Compare the served files with the hashes in section 5.1 and `tools/site-snapshots/SHA256SUMS`, because the lab can change; `lab.js` is served with `cache-control: public, max-age=14400, must-revalidate, no-transform`. 4. Run the human-tap list in the notebook (sections A to H, plus 18b and 25c) in order, so nothing is installed twice. After each "[agent reads]" step run, for example, `UDID= WIPID=PID: tools/wi-read.sh "=https://openpwa.net/lab/device-tests/t7/perm/" tools/perm7.js` with the expression named in the notebook entry. Use an exact-URL match (leading `=`) for the lab root. 5. Ask one question per message and name the screen element exactly ("the strip with the time, signal and battery"); confirm a step was done before asking for its result (lessons of A9 and A10). 6. For permission tests use a never-prompted app and record the screen; read the state before and after. 7. Recommended additions for a rerun: a T4 app whose manifest `theme_color` differs from its meta `theme-color`; the N22 cookie-timing protocol; a `notificationclick` test in which `clients.matchAll` finds no window (for example, after a device restart, or after the page has been closed long enough for iOS to discard it, verified by a read before the tap). ## 14. Data availability Public copy. This report was written before publication; the evidence it describes is published, sanitised as listed in `README.md`, at https://openpwa.net/research/2026-10-ios18-home-screen-web-apps/data/ under CC BY 4.0. Push endpoints are recorded as host only. The originals are kept outside version control. ## 15. Changelog - 2026-10-20: first version, written after the evidence archive (commit 8607f20b, notebook N57 to N63). Adds corrections C1 and C2, anomaly A11 and discrepancy D9. Supersedes the notebook's original Results summary rows for T4 ("solid black … independent of theme-color"), T7 ("probable observer miss") and T11 ("uncertain").