diff --git a/doc/share-extension-app-group-audit.md b/doc/share-extension-app-group-audit.md
new file mode 100644
index 00000000..fe7518fe
--- /dev/null
+++ b/doc/share-extension-app-group-audit.md
@@ -0,0 +1,155 @@
+# iOS App Group Configuration Audit
+
+**Generated:** 2026-06-25 17:31:15 PST
+
+## Scope
+
+Static inspection of App Group configuration for the **App** target and the **TimeSafariShareExtension** target: entitlements, capabilities, bundle identifiers, Debug/Release build settings, and signing. No code was modified.
+
+### Files Inspected
+
+| File | Role |
+|------|------|
+| `ios/App/App/App.entitlements` | App target App Group declaration |
+| `ios/App/TimeSafariShareExtension/TimeSafariShareExtension.entitlements` | Extension App Group declaration |
+| `ios/App/App.xcodeproj/project.pbxproj` | Bundle IDs, teams, signing, entitlement linkage |
+| `ios/App/App/SharedImageUtility.swift` | App Group identifier used by main app code |
+| `ios/App/TimeSafariShareExtension/ShareViewController.swift` | App Group identifier used by extension code |
+
+---
+
+## CRITICAL FINDING — Code vs Entitlements App Group Mismatch
+
+The entitlements and the Swift source declare **different** App Group identifiers:
+
+| Location | App Group identifier |
+|----------|----------------------|
+| `App.entitlements` | `group.app.trentlarson.timesafari.share` |
+| `TimeSafariShareExtension.entitlements` | `group.app.trentlarson.timesafari.share` |
+| `SharedImageUtility.swift` (`appGroupIdentifier`) | `group.app.timesafari.share` |
+| `ShareViewController.swift` (`appGroupIdentifier`) | `group.app.timesafari.share` |
+
+The runtime code targets `group.app.timesafari.share`, but **neither target is entitled to that group** — both entitlements now grant `group.app.trentlarson.timesafari.share`.
+
+This is an **uncommitted change**: `git diff` shows both entitlements were just changed from `group.app.timesafari.share` → `group.app.trentlarson.timesafari.share`, while the Swift code still uses the old value. Before this edit the code and entitlements matched; after it they do not.
+
+### Runtime Consequences
+
+- `FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.app.timesafari.share")` returns **nil** (the app is not entitled to that group). The extension's `storeImageData` aborts via `guard let containerURL` → image file is never written; the main app's reads return nil.
+- `UserDefaults(suiteName: "group.app.timesafari.share")` does **not** resolve to the shared, entitled suite. Writes fall back to each process's own preferences domain, so the extension's keys (`sharedPhotoFilePath`, `sharedPhotoShareId`, `shareExtensionLastStart`, `sharedPhotoReady`) are **not visible** to the main app.
+
+Net effect: the entire share-target handoff via the App Group breaks while this mismatch exists. This is the most likely root cause of "App Group UserDefaults writes failing."
+
+**Note:** This affects both Debug and Release (the entitlements have no per-configuration variants), not Debug only.
+
+---
+
+## Direct Answers
+
+### Do both targets declare the same App Group?
+
+**Yes — the two entitlements files match each other.** Both `App.entitlements` and `TimeSafariShareExtension.entitlements` declare exactly:
+
+```xml
+com.apple.security.application-groups
+
+ group.app.trentlarson.timesafari.share
+
+```
+
+However, **the code does not match the entitlements** (see Critical Finding). So "same App Group" is true at the entitlement level, false at the entitlement-vs-code level.
+
+### Are there any Debug vs Release differences?
+
+**Entitlements / App Group:** No. A single entitlements file per target applies to both configurations; the App Group string is identical in Debug and Release (`group.app.trentlarson.timesafari.share`).
+
+**Bundle identifiers:** Yes — they differ by configuration:
+
+| Target | Debug | Release |
+|--------|-------|---------|
+| App | `app.trentlarson.timesafari` | `app.timesafari` |
+| Extension | `app.trentlarson.timesafari.TimeSafariShareExtension` | `app.timesafari.TimeSafariShareExtension` |
+
+(The Debug bundle IDs were just changed from the `app.timesafari*` form per `git diff`.)
+
+**Development team:** Yes — differs by configuration (see next answer).
+
+In both configurations the extension bundle ID is correctly nested under the app bundle ID, which is required for an app extension.
+
+### Are there any team-ID differences that could affect App Group access?
+
+| Configuration | App team | Extension team | Match? |
+|---------------|----------|----------------|--------|
+| Debug | `7XVXYPEQYJ` | `7XVXYPEQYJ` | ✅ same |
+| Release | `GM3FS5JQPH` | `GM3FS5JQPH` | ✅ same |
+
+- **Within each configuration, both targets use the same team** — this is the condition required for two targets to share an App Group, and it is satisfied.
+- **Across configurations the teams differ** (Debug `7XVXYPEQYJ` vs Release `GM3FS5JQPH`). The Debug team was just changed from `GM3FS5JQPH` per `git diff`.
+
+Implications:
+1. The App Group container is namespaced by Team ID at runtime (`$(TeamID).group...`). A Debug install (team `7XVXYPEQYJ`) and a Release install (team `GM3FS5JQPH`) use **different physical containers** and cannot share data with each other. This is normal and only matters if you expect data continuity between Debug and Release builds.
+2. With **Automatic** signing, the App Group `group.app.trentlarson.timesafari.share` must be registered/enabled for **both** teams. If it is not provisioned under the Debug team `7XVXYPEQYJ`, automatic signing of the Debug build can fail to include the App Group entitlement (or fail to sign), which would also break App Group access in Debug.
+
+### Are there signing/entitlement mismatches that could cause App Group UserDefaults writes to fail in Debug builds?
+
+**Yes.** In order of severity:
+
+1. **(Primary) Code/entitlement group-ID mismatch.** Code uses `group.app.timesafari.share`; entitlements grant `group.app.trentlarson.timesafari.share`. The code's group is not entitled, so shared `UserDefaults`/container access fails. Affects Debug and Release.
+
+2. **(Debug-specific risk) App Group provisioning under the Debug team.** Debug now signs with team `7XVXYPEQYJ` (changed from `GM3FS5JQPH`). Under Automatic signing, if `group.app.trentlarson.timesafari.share` is not enabled for team `7XVXYPEQYJ`, the Debug build's App Group entitlement may not be granted, causing writes to silently fall back to the local domain.
+
+3. **(Consistency) Bundle-ID change accompanying the team change.** Debug bundle IDs changed to `app.trentlarson.timesafari*`. App Groups don't have to match bundle IDs, so this is not a direct cause, but combined with the new team it means Debug provisioning is a distinct profile/identifier set that must independently carry the App Group capability.
+
+No mismatch was found **between the two entitlement files themselves**, and no per-configuration entitlement override exists.
+
+---
+
+## Detailed Configuration
+
+### Entitlements (identical content in both files)
+
+```xml
+com.apple.security.application-groups
+
+ group.app.trentlarson.timesafari.share
+
+```
+
+`CODE_SIGN_ENTITLEMENTS` linkage (both Debug and Release):
+
+| Target | Entitlements path |
+|--------|-------------------|
+| App | `App/App.entitlements` |
+| Extension | `TimeSafariShareExtension/TimeSafariShareExtension.entitlements` |
+
+### Bundle Identifiers, Teams, Signing (project.pbxproj)
+
+| Setting | App Debug | App Release | Ext Debug | Ext Release |
+|---------|-----------|-------------|-----------|-------------|
+| `PRODUCT_BUNDLE_IDENTIFIER` | `app.trentlarson.timesafari` | `app.timesafari` | `app.trentlarson.timesafari.TimeSafariShareExtension` | `app.timesafari.TimeSafariShareExtension` |
+| `DEVELOPMENT_TEAM` | `7XVXYPEQYJ` | `GM3FS5JQPH` | `7XVXYPEQYJ` | `GM3FS5JQPH` |
+| `CODE_SIGN_STYLE` | Automatic | Automatic | Automatic | Automatic |
+| `CODE_SIGN_ENTITLEMENTS` | `App/App.entitlements` | same | `TimeSafariShareExtension/...entitlements` | same |
+| App Group (from entitlements) | `group.app.trentlarson.timesafari.share` | same | same | same |
+
+### App Group Identifier Used in Code
+
+```swift
+// SharedImageUtility.swift:13 and ShareViewController.swift:13
+private let appGroupIdentifier = "group.app.timesafari.share" // ← does NOT match entitlements
+```
+
+---
+
+## Recommendations (no code changed)
+
+1. **Resolve the group-ID mismatch.** Either revert the entitlements back to `group.app.timesafari.share`, or update the two Swift constants to `group.app.trentlarson.timesafari.share`. Both sides must use one identical string.
+2. **Confirm App Group provisioning per team.** Ensure `group.app.trentlarson.timesafari.share` (whichever string is chosen) is enabled for both `7XVXYPEQYJ` (Debug) and `GM3FS5JQPH` (Release) so Automatic signing includes the capability in both configurations.
+3. **Decide whether the Debug↔Release team/bundle-ID split is intentional.** If cross-config data continuity is ever expected, note that different Team IDs yield different App Group containers.
+4. **Verify at runtime** using the existing `getShareExtensionDiagnostics()` / `[ShareTarget]` logs: after aligning identifiers, `shareExtensionLastStart` written by the extension should become readable by the main app.
+
+---
+
+## Conclusion
+
+The two **entitlement files agree** on the App Group (`group.app.trentlarson.timesafari.share`) and, **within each build configuration**, both targets share the same Development Team and consistent nested bundle IDs — the structural requirements for App Group sharing are met. The decisive problem is that the **Swift code still references the old group `group.app.timesafari.share`**, which no entitlement grants; this breaks both shared `UserDefaults` and the shared container in all builds. Secondarily, the recent Debug switch to team `7XVXYPEQYJ` means the chosen App Group must be provisioned under that team for Debug App Group access to work under Automatic signing.