appstore-precheck 1.1.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +151 -0
- package/README.md +238 -55
- package/package.json +2 -2
- package/skills/appstore-precheck/SKILL.md +201 -59
- package/skills/appstore-precheck/evals/README.md +3 -2
- package/skills/appstore-precheck/evals/evals.json +6 -3
- package/skills/appstore-precheck/guidelines-baseline.json +19 -7
- package/skills/appstore-precheck/references/methodology.md +85 -7
- package/skills/appstore-precheck/references/pierre-deep-review.md +272 -0
- package/skills/appstore-precheck/scripts/scan.sh +335 -13
- package/skills/appstore-precheck/scripts/verdict.sh +3 -3
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Phase 4: Pierre deep review (28 semantic checks)
|
|
2
|
+
|
|
3
|
+
After Phase 3 (explaining every scan FAIL/WARN), Pierre runs a **read-only, project-wide
|
|
4
|
+
semantic review** of 28 guideline areas the static scanner cannot fully judge. Checks **1–22**
|
|
5
|
+
(minus Tier B items below) are high-confidence (Tier A); checks **4, 5, 7, 10, 15, and 28** are
|
|
6
|
+
heuristic advisory (Tier B v1 — higher false-positive risk, still useful pre-submit signals).
|
|
7
|
+
|
|
8
|
+
This is the **Review Simulator** layer: Pierre reads Swift, metadata, entitlements, screenshots,
|
|
9
|
+
xcstrings, paywall views, review notes, and fetches live privacy/support URLs — then cross-checks
|
|
10
|
+
claims against evidence.
|
|
11
|
+
|
|
12
|
+
**This phase does not change the GREEN/YELLOW/RED verdict.** Verdict counts come only from
|
|
13
|
+
Phases 0–2 (`FAIL:` / `WARN:` lines). Phase 4 emits `REVIEW-PASS:` or `REVIEW-FINDING:` lines
|
|
14
|
+
that Pierre explains in Phase 5 presentation.
|
|
15
|
+
|
|
16
|
+
## Rules
|
|
17
|
+
|
|
18
|
+
- **Read-only:** never modify project files.
|
|
19
|
+
- **Evidence-based:** cite `file:line`, metadata path, screenshot filename, or fetched URL text.
|
|
20
|
+
If you cannot read something (private URL, missing file), say so — do not invent findings.
|
|
21
|
+
- **All 28 checks, every run:** report each item as `REVIEW-PASS:` or `REVIEW-FINDING:` — no skipping.
|
|
22
|
+
- **REVIEW-FINDING severity:** always `WARN` (advisory). Never emit `REVIEW-FINDING: … FAIL`.
|
|
23
|
+
A deep-review issue informs the human; it does not block the token by itself.
|
|
24
|
+
- **Tier B checks (4, 5, 7, 10, 15, 28):** prefer `REVIEW-PASS: … — not applicable` when the signal is absent;
|
|
25
|
+
when flagging, use cautious language ("may trigger review questions") — these are heuristics.
|
|
26
|
+
- **Deepen scan hits:** when Phase 1 already flagged a guideline, Phase 4 still runs the matching
|
|
27
|
+
deep check and adds semantic context (do not repeat the machine line verbatim — add what the
|
|
28
|
+
scanner could not see).
|
|
29
|
+
- **WebFetch:** use `WebFetch` (or equivalent) on `privacy_url.txt` and `support_url.txt` when
|
|
30
|
+
present. If fetch fails, `REVIEW-FINDING: … WARN — could not fetch privacy policy; verify manually`.
|
|
31
|
+
- **Screenshots:** read at least one PNG/JPEG per primary locale; compare visible UI/features to
|
|
32
|
+
metadata claims.
|
|
33
|
+
- **Language:** write Pierre's 2–3 sentence explanations in the user's conversation language.
|
|
34
|
+
|
|
35
|
+
## Output format
|
|
36
|
+
|
|
37
|
+
For each of the 28 checks (in table order):
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
REVIEW-PASS: <guideline> — <one-line why it looks OK, with evidence pointer>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
or
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
REVIEW-FINDING: <guideline> WARN — <one-line concrete mismatch or gap, with evidence pointer>
|
|
47
|
+
Pierre: <2–3 sentences: why Apple cares, what you found, what to fix or verify>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If a check is **not applicable** (e.g. no HealthKit, no VPN, no contest copy), still report:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
REVIEW-PASS: <guideline> — not applicable (<reason>)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## The 28 checks (guideline order)
|
|
59
|
+
|
|
60
|
+
| # | Guideline | Deep question | Primary sources |
|
|
61
|
+
|---|-----------|---------------|-----------------|
|
|
62
|
+
| 1 | **1.2.1** | UGC present → is there a real report/block/moderation UI flow, not just keywords in copy? | Swift navigation, moderation views, §22 scan context |
|
|
63
|
+
| 2 | **1.4.1** | Health/medical/wellness claims in metadata or UI without appropriate disclaimers or HealthKit compliance? | metadata, Swift HealthKit usage, onboarding copy |
|
|
64
|
+
| 3 | **2.1** | Metadata/marketing claims match implemented features (AI, offline, ad-block, sync, etc.)? | metadata, description, Swift feature grep |
|
|
65
|
+
| 4 | **2.1** | Login-gated app → App Review demo account / notes **actionable** (credentials, steps, not placeholder)? | `review_information/`, `.reviewPrepNotes`, §31 scan context |
|
|
66
|
+
| 5 | **2.2** | Store-facing copy or UI still says beta / test / preview / work-in-progress? | metadata, release_notes, Swift UI strings |
|
|
67
|
+
| 6 | **2.3.2** | Primary category plausible for app type (game vs utility vs health, etc.)? | fastlane `primary_category`, metadata tone, code structure |
|
|
68
|
+
| 7 | **2.3.4** | App preview assets present → features shown match the shipped app and metadata? | preview video paths, metadata, Swift UI |
|
|
69
|
+
| 8 | **2.3.5** | Screenshots show features the app actually ships; no misleading device frames or competitor UI? | screenshot images, metadata, Swift UI |
|
|
70
|
+
| 9 | **2.3.6** | Pricing/subscription language in metadata matches paywall (free vs paid, trial terms)? | metadata, paywall Swift, xcstrings |
|
|
71
|
+
| 10 | **2.3.9** | Incentivized review copy ("rate 5 stars", "review for reward") in metadata or UI? | metadata, onboarding, paywall, §25 scan context |
|
|
72
|
+
| 11 | **2.3.11–2.3.13** | Cross-locale metadata consistent (feature lists, trial terms, support/privacy URLs, pricing claims)? | all `fastlane/metadata/*` locales |
|
|
73
|
+
| 12 | **3.1.1** | Digital goods sold or unlocked via external purchase links (web checkout, Stripe in WebView)? | Swift WebView/paywall, metadata, entitlements |
|
|
74
|
+
| 13 | **3.1.2** | Subscription/trial/auto-renew/cancel disclosures are **legible sentences**, not keyword stubs? | paywall views, xcstrings, String Catalog |
|
|
75
|
+
| 14 | **4.2.1–4.2.2** | App is more than a thin shell: meaningful navigation, native affordances, not a lone WebView brochure? | Swift UI structure, §12/§35 scan context |
|
|
76
|
+
| 15 | **4.5.1–4.5.3** | Push or HomeKit entitlement → used as intended (no spam-push promises; HomeKit without home UI)? | entitlements, Info.plist, metadata, Swift |
|
|
77
|
+
| 16 | **4.8** | Third-party login present → Sign in with Apple offered, or a documented exempt case (enterprise, existing account, etc.)? | login Swift, SDK imports, §14 scan context |
|
|
78
|
+
| 17 | **5.1.1(i)** | Privacy policy text (fetched) matches data collection in code, PrivacyInfo, and App Privacy narrative? | fetch privacy URL, PrivacyInfo, SDK imports |
|
|
79
|
+
| 18 | **5.1.1(ii)** | Purpose strings are specific and tied to a visible feature (not empty, generic, or copy-paste)? | Info.plist, permission usage in Swift |
|
|
80
|
+
| 19 | **5.1.1(iii)** | Data/permission requests proportionate to stated app purpose (no obvious over-collection)? | permissions, SDKs vs metadata promise |
|
|
81
|
+
| 20 | **5.1.1(iv)** | Permission denial handled gracefully — no infinite re-prompt loops or hard blocks without explanation? | location/camera/notification/auth flows in Swift |
|
|
82
|
+
| 21 | **5.1.2** | ATT prompt, `NSUserTrackingUsageDescription`, privacy policy tracking section, and ad SDK usage align? | Info.plist, policy fetch, ad SDK imports |
|
|
83
|
+
| 22 | **5.1.3** | HealthKit data not used for advertising/marketing; sync paths respect health-data rules? | HealthKit + analytics/ad SDK co-use |
|
|
84
|
+
| 23 | **5.1.4** | Kids-audience signals → parental gate before external links/purchases/account areas? | metadata kids wording, parental gate UI |
|
|
85
|
+
| 24 | **5.4** | VPN/NetworkExtension → on-screen disclosure text visible in UI strings (not only Info.plist)? | Swift strings, NetworkExtension usage |
|
|
86
|
+
| 25 | **5.2.1–5.2.3** | Obvious third-party trademark/brand misuse in metadata, assets, or UI copy? | metadata, asset filenames, Swift strings |
|
|
87
|
+
| 26 | **5.3.1–5.3.3** | Contest/sweepstakes/lottery copy → official rules/eligibility/disclosure present in metadata? | description, keywords, in-app contest UI |
|
|
88
|
+
| 27 | **5.6.2–5.6.3** | Developer identity consistent: app name, support URL content, bundle/marketing domain match? | fetch support URL, metadata, legal/footer copy |
|
|
89
|
+
| 28 | **5.6.4–5.6.7** | Rating/review manipulation dark patterns (withhold features until 5 stars, direct write-review links without `requestReview`)? | Swift, metadata, §25 scan context |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Per-check procedure (detail)
|
|
94
|
+
|
|
95
|
+
### 1 — 1.2.1 UGC moderation UI
|
|
96
|
+
|
|
97
|
+
1. If no UGC signals (posts, comments, chat, uploads), mark not applicable.
|
|
98
|
+
2. Search Swift for report/block/flag/moderate flows and screens reachable from content.
|
|
99
|
+
3. Compare to metadata promises ("community", "share", "chat").
|
|
100
|
+
4. Flag if UGC exists but moderation is only mentioned in text, not implemented in UI.
|
|
101
|
+
|
|
102
|
+
### 2 — 1.4.1 Health / medical claims
|
|
103
|
+
|
|
104
|
+
1. Scan metadata and onboarding for diagnose, treat, cure, clinical, FDA, blood pressure, etc.
|
|
105
|
+
2. If HealthKit present, check disclaimers ("not a medical device") where claims exist.
|
|
106
|
+
3. Flag unsubstantiated treatment claims without appropriate health disclaimers.
|
|
107
|
+
|
|
108
|
+
### 3 — 2.1 Claims ↔ code
|
|
109
|
+
|
|
110
|
+
1. Extract feature claims from name, subtitle, description, keywords (AI, ML, offline, block ads, VPN, etc.).
|
|
111
|
+
2. Grep Swift / imports for matching implementation.
|
|
112
|
+
3. Flag prominent metadata claims with no code evidence.
|
|
113
|
+
|
|
114
|
+
### 4 — 2.1 Review notes / demo account quality *(Tier B v1)*
|
|
115
|
+
|
|
116
|
+
1. If the app is login-gated (`SecureField`, Login/SignIn views) or scan §31 flagged missing demo:
|
|
117
|
+
read `fastlane/metadata/*/review_information/` (username, password, notes) and
|
|
118
|
+
`.appstore-precheck.json` → `reviewPrepNotes` if set.
|
|
119
|
+
2. Flag empty notes, placeholder credentials (`test`, `demo`, `changeme`, `TBD`), or notes that
|
|
120
|
+
do not explain how to reach core features (subscription paywall, Screen Time blocking, etc.).
|
|
121
|
+
3. If the app is not login-gated and has no account wall, mark not applicable.
|
|
122
|
+
|
|
123
|
+
### 5 — 2.2 Beta / test language *(Tier B v1)*
|
|
124
|
+
|
|
125
|
+
1. Grep store metadata, `release_notes.txt`, and user-visible Swift strings (not code comments) for:
|
|
126
|
+
`beta`, `testflight`, `test flight`, `preview`, `pre-release`, `work in progress`, `WIP`,
|
|
127
|
+
`under development`, `not final`, `experimental`.
|
|
128
|
+
2. Exclude legitimate internal keys and developer log strings not shown to users.
|
|
129
|
+
3. Flag any store-facing copy implying the App Store build is unfinished or a beta.
|
|
130
|
+
|
|
131
|
+
### 6 — 2.3.2 Category fit
|
|
132
|
+
|
|
133
|
+
1. Read primary category if present in fastlane or `.appstore-precheck.json`.
|
|
134
|
+
2. Infer app type from code (game loop, utility, reader, social).
|
|
135
|
+
3. Flag obvious mismatch (arcade game filed as Productivity).
|
|
136
|
+
|
|
137
|
+
### 7 — 2.3.4 App preview consistency *(Tier B v1)*
|
|
138
|
+
|
|
139
|
+
1. Look for app preview assets under `fastlane/metadata/*/preview*` or `*.mov` / `*.mp4` in metadata trees.
|
|
140
|
+
2. If no preview assets in-repo, mark not applicable (previews may live only in App Store Connect).
|
|
141
|
+
3. If previews exist: compare visible features/captions to metadata and Swift UI; flag previews
|
|
142
|
+
showing features absent from the build.
|
|
143
|
+
|
|
144
|
+
### 8 — 2.3.5 Screenshots vs reality
|
|
145
|
+
|
|
146
|
+
1. Open ≥1 screenshot per primary locale.
|
|
147
|
+
2. List visible features (tabs, paywall, login, maps, etc.).
|
|
148
|
+
3. Flag screenshots showing features absent from the build or metadata.
|
|
149
|
+
|
|
150
|
+
### 9 — 2.3.6 Pricing language
|
|
151
|
+
|
|
152
|
+
1. Compare metadata "free", trial, and price claims to paywall/subscription UI strings.
|
|
153
|
+
2. Flag "completely free" metadata when IAP/paywall exists without clear disclosure.
|
|
154
|
+
|
|
155
|
+
### 10 — 2.3.9 Incentivized review *(Tier B v1)*
|
|
156
|
+
|
|
157
|
+
1. Grep metadata, onboarding, and paywall strings for: `rate us`, `leave a review`, `5 star`,
|
|
158
|
+
`five star`, `review and get`, `gift card`, `reward for review`, `write a review to unlock`.
|
|
159
|
+
2. Cross-check scan §25 (custom review prompt) — Phase 4 adds semantic context if §25 passed.
|
|
160
|
+
3. Flag quid-pro-quo review incentives or star-rating manipulation copy.
|
|
161
|
+
|
|
162
|
+
### 11 — 2.3.11–2.3.13 Locale parity (semantic)
|
|
163
|
+
|
|
164
|
+
1. Beyond scan's file-presence check: compare trial terms, feature bullets, and pricing claims across locales.
|
|
165
|
+
2. Flag material omissions (trial mentioned in en-US only, different feature lists).
|
|
166
|
+
|
|
167
|
+
### 12 — 3.1.1 External digital purchase
|
|
168
|
+
|
|
169
|
+
1. Search for external checkout URLs, Stripe/PayPal in WebView, "subscribe on our website".
|
|
170
|
+
2. Flag digital unlocks that bypass StoreKit without 3.1.1(a) entitlement context.
|
|
171
|
+
|
|
172
|
+
### 13 — 3.1.2 Disclosure quality
|
|
173
|
+
|
|
174
|
+
1. Read paywall disclosure strings (Swift + xcstrings).
|
|
175
|
+
2. Flag if trial/auto-renew/cancel info is a single keyword, lorem, or unreadably dense.
|
|
176
|
+
3. Require human-readable sentences covering trial length, renewal, and cancellation path.
|
|
177
|
+
|
|
178
|
+
### 14 — 4.2.1–4.2.2 Minimum functionality (semantic)
|
|
179
|
+
|
|
180
|
+
1. Map primary user journeys (launch → core action).
|
|
181
|
+
2. Flag single-screen WebView brochure, template placeholder flows, or no native navigation beyond §12 minimum.
|
|
182
|
+
|
|
183
|
+
### 15 — 4.5.1–4.5.3 Push / HomeKit abuse *(Tier B v1)*
|
|
184
|
+
|
|
185
|
+
1. Read entitlements and Info.plist for push notifications and HomeKit.
|
|
186
|
+
2. **Push:** if push entitlement present, scan metadata/UI for spam patterns ("notify every hour",
|
|
187
|
+
"unlimited reminders") unrelated to user-initiated alerts.
|
|
188
|
+
3. **HomeKit:** if HomeKit framework imported, confirm home-automation UI exists; flag HomeKit
|
|
189
|
+
import with no home-related features (possible misuse).
|
|
190
|
+
4. If neither push nor HomeKit signals, mark not applicable.
|
|
191
|
+
|
|
192
|
+
### 16 — 4.8 Sign in with Apple (context)
|
|
193
|
+
|
|
194
|
+
1. If Google/Facebook/etc. login exists, confirm `ASAuthorizationAppleID` / Sign in with Apple button.
|
|
195
|
+
2. If absent, assess exempt patterns (enterprise-only, password-only existing users) — flag uncertain cases for human review.
|
|
196
|
+
|
|
197
|
+
### 17 — 5.1.1(i) Privacy policy accuracy
|
|
198
|
+
|
|
199
|
+
1. Fetch privacy URL from primary locale metadata.
|
|
200
|
+
2. Compare policy statements to: PrivacyInfo collected types, tracking domains, location/camera/health SDK usage.
|
|
201
|
+
3. Flag direct contradictions ("we do not collect location" + CoreLocation).
|
|
202
|
+
|
|
203
|
+
### 18 — 5.1.1(ii) Purpose string quality
|
|
204
|
+
|
|
205
|
+
1. List every `NS*UsageDescription` in Info.plist.
|
|
206
|
+
2. Flag empty, placeholder, or generic strings; flag mismatch with the feature that triggers the prompt.
|
|
207
|
+
|
|
208
|
+
### 19 — 5.1.1(iii) Data minimization
|
|
209
|
+
|
|
210
|
+
1. List sensitive permissions and SDKs.
|
|
211
|
+
2. Compare to app category and metadata promise.
|
|
212
|
+
3. Flag obvious overreach (contacts + photos + location for a calculator).
|
|
213
|
+
|
|
214
|
+
### 20 — 5.1.1(iv) Permission denial UX
|
|
215
|
+
|
|
216
|
+
1. Trace location/camera/photo/notification permission flows.
|
|
217
|
+
2. Flag forced loops, dead-ends, or dark patterns after denial.
|
|
218
|
+
|
|
219
|
+
### 21 — 5.1.2 ATT consistency
|
|
220
|
+
|
|
221
|
+
1. If ad/attribution SDK or IDFA access: confirm ATT API usage and description text.
|
|
222
|
+
2. Cross-check privacy policy tracking section vs implementation.
|
|
223
|
+
|
|
224
|
+
### 22 — 5.1.3 HealthKit + ads
|
|
225
|
+
|
|
226
|
+
1. If HealthKit imported: search for analytics/ad SDK sending health-derived signals.
|
|
227
|
+
2. Flag health data paths combined with advertising identifiers.
|
|
228
|
+
|
|
229
|
+
### 23 — 5.1.4 Parental gate
|
|
230
|
+
|
|
231
|
+
1. If kids metadata or child-audience copy: find parental gate before web/links/IAP/account.
|
|
232
|
+
2. Flag child positioning without age gate UI.
|
|
233
|
+
|
|
234
|
+
### 24 — 5.4 VPN disclosure UI
|
|
235
|
+
|
|
236
|
+
1. If NetworkExtension/NEVPNManager: search UI strings for data-collection disclosure required at launch/settings.
|
|
237
|
+
2. Flag VPN capability with no user-visible disclosure copy.
|
|
238
|
+
|
|
239
|
+
### 25 — 5.2.1–5.2.3 IP / trademarks
|
|
240
|
+
|
|
241
|
+
1. Scan metadata and visible strings for other companies' brands used as if owned.
|
|
242
|
+
2. Flag likely trademark misuse (not generic descriptive use).
|
|
243
|
+
|
|
244
|
+
### 26 — 5.3.1–5.3.3 Contests
|
|
245
|
+
|
|
246
|
+
1. If sweepstakes/contest/giveaway language: check for rules, eligibility, sponsor, no-purchase-necessary.
|
|
247
|
+
2. Flag contest marketing without rules in metadata or in-app.
|
|
248
|
+
|
|
249
|
+
### 27 — 5.6.2–5.6.3 Developer identity
|
|
250
|
+
|
|
251
|
+
1. Fetch support URL; confirm it resolves and shows developer contact or support path.
|
|
252
|
+
2. Compare app name, support domain, and privacy policy domain for consistency.
|
|
253
|
+
3. Flag placeholder support pages or identity mismatch.
|
|
254
|
+
|
|
255
|
+
### 28 — 5.6.4–5.6.7 Rating manipulation *(Tier B v1)*
|
|
256
|
+
|
|
257
|
+
1. Grep Swift and metadata for: `itms-apps://` write-review URLs, `apps.apple.com/.../write-review`,
|
|
258
|
+
"rate 5 stars", "only enable after review", custom star-rating UI tied to App Store review.
|
|
259
|
+
2. Compare to system `requestReview` / `SKStoreReviewController` usage (scan §25).
|
|
260
|
+
3. Flag dark patterns that manipulate ratings or bypass Apple's review prompt API.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Phase 5 presentation
|
|
265
|
+
|
|
266
|
+
After Phase 4, include in the final report:
|
|
267
|
+
|
|
268
|
+
1. Trilingual verdict block (from scan counts only).
|
|
269
|
+
2. Phase 3 commentary (every scan FAIL/WARN).
|
|
270
|
+
3. Phase 4 summary table: 28 checks → count of `REVIEW-FINDING` vs `REVIEW-PASS` (note Tier B items 4, 5, 7, 10, 15, 28 if any fired).
|
|
271
|
+
4. Phase 4 detail: every `REVIEW-FINDING` with Pierre explanation; optionally list `REVIEW-PASS` lines compactly.
|
|
272
|
+
5. Verbatim Phase 1 scan output + verdict/token action.
|