sg-p1-open-house — Fix-Ready Issue
_Last updated: 2026-08-04. Every claim below re-verified this pass against state/asc.json, state/asc-review.json, state/fleet.json, state/runs/, bin/fleet-status.sh, bin/fleet-build.sh, bin/fleet-dashboard.sh, and the app source at /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/._
> 2026-08-04 re-verification — nothing has been remediated; all steps below are still outstanding.
> Re-checked live this pass against state/asc.json (now generated 2026-08-03T19:15:04Z): ASC still REJECTED at build 1 / v1.0.0; sg-p1-open-house.in_flight is still JSON null while pending_review_build: 8, pending_review_ipa, pending_review_since: "2026-07-31T20:50:06Z" and last_upload_status: "held_for_review" remain set, so fleet publish still exits 65 (Step 1 not done); the same six apps — sumsum, tocktock, kid-soothe, SpellaPanda, some-singapore, within2km — still hold the literal string "null" (Step 2 not done); bin/fleet-dashboard.sh:898 is still the unguarded map(select(.value.in_flight != null)) (Step 3 not done); ios/project.yml:52 still declares NSLocationWhenInUseUsageDescription with no CLLocationManager anywhere in the target (Step 4 not done). Confirmed still-true and requiring no action: the jq -e 'true' fix is present at bin/fleet-status.sh:95 with its explanatory comment, and ios/project.yml remains at MARKETING_VERSION: "1.1.0" / CURRENT_PROJECT_VERSION: "8" with TARGETED_DEVICE_FAMILY: "1,2" — do not re-bump.
>
> This pass also re-confirmed the doctor verdict is unchanged: diagnosis Not auto-healing (fixable=false, autoHeal=true); verify verdict {"isRealFix": false, "confidence": "high", "reasoning": "No fix attempted"}. No app code was modified in this pass — this file is the deliverable.
Status (one line)
UNHEALTHY — App Store state is REJECTED under Guideline 4.2 (Minimum Functionality), and it was not auto-healed because the fix is not mechanical (fixable=false, autoHeal=true): clearing a 4.2 rejection requires a human to upload an IPA *and* write a Resolution Center reply explaining what changed. Verify verdict isRealFix=false ("No fix attempted", confidence high) is correct — no fix was attempted.
- App:
com.zzxwill.sgp1openhouse· ASC ID6778872421· ASC name "P1 Open House SG" - Last known-good build number:
1— per/Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/state/asc.json:latest_build_number: "1",latest_build_state: "VALID", uploaded2026-06-10T08:39:06-07:00,build_count: 1,version_string: "1.0.0".
Caveat — there is no clean build to roll back to. Build 1 is the *only* build Apple has ever received, and it is the build that was rejected. "Known-good" here means it compiled and validated (VALID), not that it passed review.
- Local
state/fleet.jsonsayslast_build_number: 8,last_build_status: "success",last_upload_status: "held_for_review". The gap between local 8 and ASC 1 is the incident — builds 2–8, i.e. every improvement since June, have never reached Apple.
Root cause & evidence
Two distinct problems are layered. A is the reason the app is unhealthy; B is why the recommended recovery command currently cannot run.
A. PRIMARY — the Guideline 4.2 rejection was never answered
Failing log line — verbatim from .apps["sg-p1-open-house"].message.latest.body in state/asc-review.json (message dated 2026-06-15T20:09:57.833Z, from APPLE):
Guideline 4.2 - Design - Minimum Functionality
Issue Description
The usefulness of the app is limited by the minimal functionality it currently provides.
Specifically, the app does not provide sufficient content and features to be useful, unique, and "app-like."
Apps should provide valuable utility or entertainment, draw people in by offering compelling
capabilities or content, or enable people to do something they couldn't do before or in a way
they couldn't do it before.
Next Steps
We encourage you to review the app concept and incorporate different content and features that
are in compliance with the App Review Guidelines.
The same record also pins the review environment — note it was not reviewed on an iPhone:
Submission ID: 4d645264-2408-4178-9111-2054ba6276a0
Review date: June 15, 2026
Review Device: iPad Air 11-inch (M3)
Version reviewed: 1.0.0 (1)
Corroborating state in state/asc.json:
"sg-p1-open-house": {
"asc_id": "6778872421",
"latest_build_number": "1",
"latest_build_state": "VALID",
"build_count": 1,
"app_store_state": "REJECTED",
"version_string": "1.0.0"
}
Review metadata (state/asc-review.json): reviewState: UNRESOLVED_ISSUES, summary {errors:1, warnings:0, infos:1, blocking:1}, Submission ID 4d645264-2408-4178-9111-2054ba6276a0, reviewed June 15 2026 on an iPad Air 11-inch (M3), version reviewed 1.0.0 (1). Note the top-level submissionId field is null; the ID above comes from the message body.
A 4.2 rejection is cleared by *explaining what changed*, not by silently resubmitting a binary. That is why autoHeal could not act.
Do not re-do this analysis — the remediation already exists. An earlier triage pass hypothesized "a phone layout stretched onto iPad." That is already handled and was re-confirmed this pass: ios/project.yml:71 sets TARGETED_DEVICE_FAMILY: "1,2" # iPhone + iPad, and v1.1.0 (MARKETING_VERSION: "1.1.0" at ios/project.yml:15) adds Info/Map/Upcoming tabs, filters + sort, and an iPad sidebar layout for exactly this resubmission. The work is done; it has simply never been uploaded. This strengthens the conclusion that the fix is getting build 8 in front of review.
One real defect still shipping in build 8 — an unused permission declaration. ios/project.yml:52 declares NSLocationWhenInUseUsageDescription ("Show your location on the map so you can see which P1 open-house schools are near you"), but no CLLocationManager exists anywhere in the target — re-verified this pass: grep -rn CLLocationManager ios/ (excluding build/) returns nothing. Only passive CLLocationCoordinate2D structs are used. A reviewer sees a permission prompt the app never honors, which actively reinforces a 4.2 finding. Resolve it either way — implement it or delete the key — before resubmitting.
B. SECONDARY BLOCKER — state is incoherent, so fleet publish exits 65
state/fleet.json currently holds in_flight: null while pending_review_build: 8, pending_review_ipa, pending_review_since and last_upload_status: "held_for_review" are all still set. bin/fleet-build.sh:92-95 gates publishing on in_flight == "pending_review":
pend=$("$BIN/fleet-status.sh" get "$APP_NAME" in_flight 2>/dev/null || echo "")
rev_build=$("$BIN/fleet-status.sh" get "$APP_NAME" pending_review_build 2>/dev/null || echo "")
if [ "$pend" != "pending_review" ]; then
echo "fleet publish: $APP_NAME has nothing held for review (in_flight=$pend)." >&2
echo "Build it first with: fleet build $APP_NAME" >&2
exit 65
fi
Confirmed live this pass: bin/fleet-status.sh get sg-p1-open-house in_flight prints nothing, so fleet publish sg-p1-open-house exits 65 — the recommended next action cannot execute. Per fleet-build.sh:235-238, the correct terminal state after a build held for review is in_flight = "pending_review", not null. Root cause: "clear the stale *building* lock" was conflated with "clear in_flight entirely."
Upstream cause of the stale lock (already fixed, verify only). bin/fleet-status.sh used jq -e . to test whether a value was JSON. jq -e derives its exit status from the truthiness of its output, not parse success — so the valid JSON literals null and false exit 1 and fell through to the bare-string branch, storing the *text* "null", a truthy value. Locks therefore appeared cleared but stayed parked. This was fleet-wide: fleet-build.sh calls set <app> in_flight 'null' at lines 77, 102, 109 and 219, so build-failure, publish-failure, publish-success and dry-run cleanup were all writing the corrupt string. Evidence the symptom was previously papered over rather than fixed: bin/fleet-dashboard.sh:45 already carries a defensive .in_flight != "null" string comparison. Now fixed at bin/fleet-status.sh:95 to jq -e 'true' (verified present in the working tree, with an explanatory comment at lines 90-94).
Residual corruption confirmed this pass. Six apps still hold the literal string "null" in state/fleet.json: sumsum, tocktock, kid-soothe, SpellaPanda, some-singapore, within2km.
Evidence — the stalled run that started this
state/runs/sg-p1-open-house-2026-07-26T20-55-06Z.log is 119 bytes, two lines, nothing else (byte count verified):
[04:55:06] starting build for sg-p1-open-house (commit 23032d0, tool make, dry_run=0)
[04:55:06] bumping build 7 → 8
It dies between the in-flight status writes and the say "running xcodegen" line, which never printed. A suspected xcodegen hang is not the cause — xcodegen generate completes in ~78ms. Most consistent with host sleep/reboot mid-run, or the harness timeout seen in state/runs/launchd-doctor.err (Background tasks still running after 600s; terminating.). Caveat: this is inferred, not proven — no pmset -g log evidence was gathered, and no hardening guard was added to fleet-build.sh.
Artifacts are genuinely fresh, not a cached false positive (all dated Aug 1 04:49, re-listed this pass): archive and export succeeded, CFBundleVersion=8 / CFBundleShortVersionString=1.1.0, IPA at /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/ios/build/Export/SgP1OpenHouse.ipa (753,518 bytes). No upload/altool has been run.
Claude-ready fix prompt
> You are fixing fleet state for sg-p1-open-house in /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/.
>
> Do not modify the app repo at /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/ except where Step 4 explicitly says so. ios/project.yml is committed at CURRENT_PROJECT_VERSION: "8" and is byte-identical in the worktree — do not revert it to "7" to re-bump. Rewriting committed version state to re-derive the same number is pure risk for no gain; build 8 already exists and is signed.
>
> Step 1 — unblock publish (required first). Restore the coherent held-for-review state:
> ```
> bin/fleet-status.sh set sg-p1-open-house in_flight '"pending_review"'
> ```
> Do not clear pending_review_build (8), pending_review_ipa, pending_review_since, or last_upload_status ("held_for_review") — they are correct and fleet publish consumes them. Verify bin/fleet-status.sh get sg-p1-open-house in_flight prints pending_review and that fleet publish sg-p1-open-house no longer exits 65.
>
> Step 2 — backfill the six apps still holding the corrupt string. The jq -e 'true' fix only stops *new* corruption. Rewrite the literal text "null" to real JSON null for sumsum, tocktock, kid-soothe, SpellaPanda, some-singapore, within2km in state/fleet.json — and for within2km also in pending_review_build, pending_review_ipa, pending_review_since. Do it in a single jq pass writing to a temp file then mv, matching the existing jq_update pattern. Type-check rather than eyeball: jq '[.apps[] | select(.in_flight == "null")] | length' must return 0.
>
> Step 3 — unify the dashboard filter. bin/fleet-dashboard.sh:898 uses select(.value.in_flight != null) *without* the string guard present at line 45 (.in_flight != null and .in_flight != "null" and .in_flight != ""). Give line 898 the same predicate so corrupted or blank entries never render as in-flight. Lines 82, 803 and 929 use // "null" defaults — leave them, but confirm they read correctly once state is real JSON null.
>
> Step 4 — resolve the unused location permission (the only in-scope app-repo change). In /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/ios/project.yml:52, either delete NSLocationWhenInUseUsageDescription or implement a real CLLocationManager behind it. Do not leave a declared permission the app never requests — it feeds the 4.2 rejection. If you change this, the build must be re-archived and the build number re-bumped through the normal fleet build path.
>
> Step 5 — optional hardening. jq -e 'true' accepts multi-token input (e.g. 1 2), which then fails at --argjson. set -e plus tmp-file-then-mv means state is never corrupted (verified: state preserved, mv never runs), but the script aborts loudly instead of falling back to the string branch. To tighten, use jq -e -n 'input' <<<"$value" or jq --slurp 'length==1'. Low severity — all current call sites pass single literals.
>
> Step 6 — disclose the full diff. bin/fleet-build.sh is modified in the working tree (comment-only, documenting a separate SynCal stale-export issue) and was omitted from a previous review. Include it in any diff you present.
>
> Do not attempt the human actions below. Uploading the IPA, replying in Resolution Center, and creating version 1.1.0 in ASC are console/human steps. Stop after Step 6 and report.
Files in scope
| File | Change |
|---|---|
| /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/state/fleet.json | sg-p1-open-house.in_flight → "pending_review"; backfill string "null" → JSON null for the six apps |
| /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/bin/fleet-status.sh | line 95, jq -e . → jq -e 'true' — already applied, verify only |
| /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/bin/fleet-dashboard.sh | line 898, add the string-"null" guard from line 45 |
| /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apple-apps-fleet/bin/fleet-build.sh | no functional change; include the existing comment-only edit in the diff |
| /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/ios/project.yml | line 52 — remove or implement NSLocationWhenInUseUsageDescription |
| everything else under /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/ | none — do not modify |
Human / console actions (no code fix resolves these)
fleet publish sg-p1-open-house— works only after Step 1 — or upload/Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/ios/build/Export/SgP1OpenHouse.ipamanually.- Reply in Resolution Center on submission
4d645264-2408-4178-9111-2054ba6276a0, explaining the functionality added since the reviewed 1.0.0 (1). A 4.2 rejection is cleared by explanation, not by silent resubmission. - In ASC (app id
6778872421, still showing 1.0.0): create version 1.1.0, attach build 8, refresh screenshots including iPad, and resubmit.
Reference (quick facts)
| Item | Value | Source |
|---|---|---|
| Last known-good build number | 1 — VALID, v1.0.0, uploaded 2026-06-10T08:39:06-07:00, build_count: 1 | state/asc.json |
| ASC app state | REJECTED | state/asc.json |
| Bundle ID / ASC ID / ASC name | com.zzxwill.sgp1openhouse · 6778872421 · "P1 Open House SG" | state/asc.json |
| Local build held for review | 8 (last_build_status: "success", last_upload_status: "held_for_review", held since 2026-07-31T20:50:06Z) | state/fleet.json |
| Held IPA | /Users/zzxwill/Programming/go/src/zzxwill/apple-apps/apps/sg-p1-open-house/ios/build/Export/SgP1OpenHouse.ipa | state/fleet.json |
| Submission ID | 4d645264-2408-4178-9111-2054ba6276a0 (UNRESOLVED_ISSUES, submitted 2026-06-10T15:53:42.137Z) | state/asc-review.json |
| Guideline | 4.2 — Design — Minimum Functionality | state/asc-review.json |
| Review device / version reviewed | iPad Air 11-inch (M3) · 1.0.0 (1) | state/asc-review.json |
| Diagnosis | Not auto-healing (fixable=false, autoHeal=true) | fleet doctor |
| Verify verdict | isRealFix=false, confidence high — "No fix attempted" | fleet doctor |
Note on "known-good": build 1 is the *only* build Apple has ever received, and it is the rejected one. There is no clean build to roll back to — 1 means "compiled and validated," not "passed review." Recovery is forward (build 8 + a Resolution Center reply), not backward.