blind-bag-spells (FunSpellPanda) — Fix-Ready Issue
_Filed: 2026-07-12 · Re-verified: 2026-08-04 · Diagnosis: "Not auto-healing (fixable=false, autoHeal=true)" · Verify verdict: {"app":"blind-bag-spells","isRealFix":false,"confidence":"high","reasoning":"No fix attempted","risks":""}_
Status (one line)
Unhealthy: App Store state is REJECTED (App Review launch crash, two ASC-console metadata items, and the attached build has expired), and it was NOT auto-healed because the doctor classified the blocker as fixable=false at whole-app granularity while autoHeal=true — so no fix was ever attempted (verifier: "No fix attempted"), even though the underlying data bug is trivially fixable.
Last Known-Good Build Number
Build 11 — from /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/state/asc.json (generated_at: 2026-08-03T19:15:04Z):
"blind-bag-spells": {
"bundle_id": "com.zhouzhengxi.blindbagspells",
"asc_id": "6763868165",
"asc_name": "FunSpellPanda",
"in_prod": true,
"latest_build_number": "11",
"latest_build_state": "VALID",
"latest_uploaded": "2026-06-16T06:37:02-07:00",
"build_count": 11,
"app_store_state": "REJECTED",
"version_string": "1.0.0"
}
That is build 11, state VALID, uploaded 2026-06-16, version 1.0.0. "Known-good" here means only *accepted by ASC upload processing* — it is not review-approved (app_store_state: REJECTED).
> Numbering caveat — read before building. Local fleet state (state/fleet.json) records pending_review_build: 13, but build 13 was only ever built and held locally (last_upload_status: "held_for_review"); it never reached ASC, which still tops out at 11. project.yml line 10 also says CURRENT_PROJECT_VERSION: "13". Pick the next build number above the ASC maximum (11) *and* above the local maximum (13) — i.e. 14 — so nothing collides.
Root Cause & Evidence
Root cause: the app's bundled word list, BlindBagSpells/vocabulary.json, is a symlink to ../../shared-data/vocabulary.json whose target no longer exists (apple-apps/shared-data/ is empty). Per the app's CLAUDE.md, shared-data/vocabulary.json is the intended single source of truth shared with SpellaPanda, and xcodegen bundles the resolved symlink as a resource. With the target gone the build still "succeeds", the resource silently never lands in the bundle, Vocabulary.loadFromBundle() returns [], and the reviewed build (1.0.0 build 5, which predates today's empty-pool guard) crashed on the Play screen — producing Apple's automated launch-crash rejection. The doctor saw an App-Review rejection (a state it cannot clear on its own) and marked the whole app fixable=false, so auto-heal was skipped along with the mechanical part.
Failing log line (Apple, automated — quoted verbatim from state/asc-review.json → apps["blind-bag-spells"].message.thread[2].body):
> "App Review Guideline Issue
>
> This is an automated message. The review of this submission cannot proceed. See below for more information.
>
> The app crashed after the initial launch. Apps that crash negatively impact users.
>
> Test the app on supported devices to identify and resolve crashes and stability issues before resubmitting for review.
>
> Learn more about testing a release build."
Latest Apple message on the thread (message.thread[1].body):
> "Hello,
>
> The issues we previously identified still need your attention. … Submission ID: 9f71bdba-9903-49dc-9495-dfcb6279e65c · Review date: April 27, 2026 · Review Device: iPad Air 11-inch (M3) and iPhone 17 Pro Max · Version reviewed: 1.0.0 (5)"
>
> "Guideline 2.3.6 - Performance - Accurate Metadata … The content description selected for the app's Age Rating indicates that the app includes In-App Controls. However, we were unable to find either Parental Controls or Age Assurance mechanisms in the app."
Secondary failing line from the fleet runner (state/runs/launchd-blindbagspells.err):
> fleet-build: missing /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/blind-bag-spells/.fleet/config.sh
(note the path — it points at the stray apple-apps/blind-bag-spells/ directory, which contains only an empty BlindBagSpells.xcodeproj, not the real source at apple-apps/apps/blind-bag-spells/.) The same log also shows repeated jq: parse error: Expected separator between values at line 14, column 12.
Evidence re-verified firsthand in the working tree today (2026-08-04):
- Broken data symlink (still live).
ls -la apps/blind-bag-spells/BlindBagSpells/vocabulary.json→lrwxr-xr-x … vocabulary.json -> ../../shared-data/vocabulary.jsontest -eon it → DANGLING (does not resolve);apple-apps/shared-data/contains 0 files.- Intact donor copy still present:
apps/SpellaPanda/vocabulary.json(19693 bytes,version: 1, 258 words = 103k2+ 155p1; every entry carriesword/emoji/theme/level, matching theVocabWord+Vocabulary.FileCodable shape exactly).
- The held build 13 IPA ships without the word list. Do NOT publish it.
build/export/BlindBagSpells.ipa(605822 bytes, exported 2026-06-30);unzip -l … | grep -ic vocab→0. BlindBagSpells/Vocabulary.swiftloadFromBundle()(lines 35–47) returns[]when the resource is missing; theassertionFailureis compiled out of Release, so the failure ships silently.BlindBagSpells/PlayView.swiftHAS since been hardened — line 87guard !pool.isEmpty else { return Self.fallbackWord }(fallback declared line 83). A new build would therefore no longer crash, but would be degraded to the single fallback word "spell". The reviewed build 5 predates this guard, hence the crash.- The Guideline 2.1(a) Sign-in-with-Apple fix is present in source (
BlindBagSpells/AuthStore.swift: line 23guard error == nil else { return }; the.failurebranch at lines 35–43 setslastError = niland returns). Keep it. project.ymlline 9MARKETING_VERSION: "1.0.0", line 10CURRENT_PROJECT_VERSION: "13".- Local fleet state (
state/fleet.json):last_upload_status: "held_for_review",pending_review_build: 13,pending_review_since: 2026-06-29T20:52:25Z, last commit builtd76e93b. Latest run log (state/runs/blind-bag-spells-2026-06-29T20-50-55Z.log) endsBUILD READY — held for your review (build 13). - Submission
9f71bdba-9903-49dc-9495-dfcb6279e65cstillUNRESOLVED_ISSUES, submitted 2026-04-27T15:07:21.108Z.checksreports 2 blocking errors:"build is expired"(remediation: "Select a non-expired build for this version in App Store Connect") and"Latest review submission has unresolved issues in App Review".nextAction: "Select a non-expired build for this version in App Store Connect".
Two corrections to earlier revisions of this file
- The launch crash is already patched; the data bug is not. Because
PlayView.swift:87now guards the empty pool, a rebuild without the data fix would *pass* review while being functionally empty — every round shows only the fallback word "spell". That is arguably worse than crashing, because it ships. Fix the data; do not rely on the guard. - Commit
d76e93b("suppress benign Sign in with Apple failures … auto-healed by fleet-doctor") contains no Swift change. Its diff touches onlyCURRENT_PROJECT_VERSION11→12 inproject.ymlandproject.pbxproj, despite the commit message describing anAuthStoreedit. The actual SIWA fix landed earlier, inc2e4cf9. Guideline 2.1(a) is therefore already satisfied in source; it needs a build carrying it to reach ASC, not another patch.
Non-code rejection items still open in App Store Connect (why part of this is genuinely fixable=false for an agent):
- Guideline 2.3.6 (Accurate Metadata / Parental Controls): the Age Rating declares In-App Controls the app does not have → set Parental Controls = None on the App Information page.
- Guideline 2.1 / 1.3 (Kids data practices): the developer replied 2026-06-16T13:30:56Z (no analytics, no ads, no sharing, fully on-device —
message.thread[0]); Apple has not acknowledged that reply on the thread. Confirm or re-send. - The build attached to version 1.0.0 has expired — a fresh build must be uploaded and selected regardless of the code fix.
Claude-Ready Fix Prompt
> Goal: Restore the missing vocabulary data so the app actually has words, cut a fresh build (do NOT publish held build 13 — its IPA verifiably lacks vocabulary.json), clear the ASC console items, and resubmit version 1.0.0.
>
> Source directory: /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/blind-bag-spells/
>
> 1. Restore the vocabulary data (this is the root cause — do this first). /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/blind-bag-spells/BlindBagSpells/vocabulary.json is a dangling symlink to ../../shared-data/vocabulary.json. An intact, schema-compatible copy exists at /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/SpellaPanda/vocabulary.json (258 words). Either:
> - Restore the shared source (preferred — it is the documented design in the app's CLAUDE.md, and SpellaPanda depends on it): cp /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/SpellaPanda/vocabulary.json /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/shared-data/vocabulary.json, then confirm the symlink resolves (test -e /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/blind-bag-spells/BlindBagSpells/vocabulary.json); or
> - De-symlink: replace BlindBagSpells/vocabulary.json with a real copy of that file and update CLAUDE.md, which currently documents the shared-data symlink scheme.
> - Also address why it vanished: /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/shared-data/ is not a git repo, so the single source of truth for two shipping apps is untracked and unrecoverable if deleted again. Put it under version control (or vendor a real copy into each app) and state which you chose.
> 2. Prove the file lands in the bundle. After make export (the Makefile is the canonical entry point — do not call xcodebuild directly), run unzip -l /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/blind-bag-spells/build/export/BlindBagSpells.ipa | grep -i vocab and require a hit; then extract it and confirm it decodes to a non-empty words array. This check currently returns 0 matches and is the single most important regression gate here — the whole bug is that the broken symlink silently failed to bundle.
> 3. Keep the existing hardening — verify, do not re-break:
> - /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/blind-bag-spells/BlindBagSpells/PlayView.swift line 87: guard !pool.isEmpty else { return Self.fallbackWord } must stay (fallback declared line 83).
> - /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/blind-bag-spells/BlindBagSpells/AuthStore.swift: line 23 guard error == nil else { return }; the .failure branch (lines 35–43) must stay silent so a cancelled Sign in with Apple shows no error UI.
> - Make the silent failure loud in /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/blind-bag-spells/BlindBagSpells/Vocabulary.swift loadFromBundle() (lines 35–47): assertionFailure is stripped in Release, which is precisely why this shipped unnoticed. Replace it with something that also fails in Release (e.g. fatalError, or preconditionFailure under #if !DEBUG), or add a unit test asserting Vocabulary.all.count > 0 and that both Level.k2 and Level.p1 return non-empty arrays. The test is the cheapest durable guard.
> 4. Fix the fleet runner path error (secondary): /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/state/runs/launchd-blindbagspells.err ends with fleet-build: missing /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/blind-bag-spells/.fleet/config.sh. The runner points at the stray apple-apps/blind-bag-spells/ directory instead of apple-apps/apps/blind-bag-spells/. Correct the fleet config / launchd plist path, and consider deleting the stray directory. The same log also shows repeated jq: parse error: Expected separator between values lines — check the JSON the runner feeds jq.
> 5. Smoke-test on the reviewer's environment. Review used iPad Air 11-inch (M3) and iPhone 17 Pro Max; version reviewed 1.0.0 (5). On the nearest simulators: cold-launch, open Play, spell several words across multiple bag-opens (confirm real words, not the "spell" fallback), then trigger Sign in with Apple and cancel — no error UI, app stays usable signed-out. (See the test-ios-app skill.)
> 6. Cut a fresh build, numbered 14. ASC's highest build is 11 (last known-good upload, VALID, 2026-06-16) but a local build 13 was already produced and held, and project.yml line 10 already reads CURRENT_PROJECT_VERSION: "13" — so bump to "14" to clear both. Keep project.yml and BlindBagSpells.xcodeproj/project.pbxproj in sync, run make generate, then build/export. Do NOT publish the currently held build 13 IPA. A fresh upload is required anyway: ASC reports the currently attached build as expired.
> 7. Clear the ASC console items (no code): App Information → Age Rating → Parental Controls = None (2.3.6); confirm the kids-data-practices reply (2.1 / 1.3) was acknowledged and re-send if not; confirm App Privacy declarations state no data collected / on-device only; select the new non-expired build for version 1.0.0.
> 8. Attach the new build to the 1.0.0 submission (submission 9f71bdba-9903-49dc-9495-dfcb6279e65c, state UNRESOLVED_ISSUES) and resubmit for review.
Process note (worth fixing separately)
This app has produced ~13 near-identical re-diagnoses over three weeks with zero convergence. fixable=false is computed per-app rather than per-blocker, so an ASC-console blocker vetoes the code-level fix in the same app. Splitting that classification would let the symlink restore proceed automatically while the console items stay queued for a human. Related: fleet-doctor.sh never reads pending_review_since, so builds waiting on a human render as unexplained reds.
_No app code was changed while filing this. Evidence re-verified firsthand against the working tree, state/asc.json, state/asc-review.json, and the held IPA._