appstore-precheck 1.10.0 → 1.12.0
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
CHANGED
|
@@ -5,6 +5,70 @@ All notable changes to this project are documented here. Versioning follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.12.0] - 2026-07-02
|
|
9
|
+
|
|
10
|
+
Roadmap #5 (final roadmap item): an **optional, opt-in local dynamic simulator tier** —
|
|
11
|
+
agent-mode, advisory, and off by default. It never changes the verdict and never runs
|
|
12
|
+
in the offline CLI / npx / GitHub-Action path.
|
|
13
|
+
|
|
14
|
+
When the user explicitly asks for a dynamic check and supplies a built app (or a booted
|
|
15
|
+
simulator UDID + bundle id), the host model can run the app on a disposable simulator via
|
|
16
|
+
`xcrun simctl` + Maestro MCP tools and observe real behavior a static scan cannot — launch
|
|
17
|
+
without crashing (2.1), core screen reachable, paywall actually renders with terms (3.1.2),
|
|
18
|
+
permission prompt matches its `Info.plist` purpose string (5.1.1), a demo/login path works
|
|
19
|
+
(2.1), and live UI matches the marketing screenshots (2.3.5). It emits advisory
|
|
20
|
+
`DYNAMIC-PASS:` / `DYNAMIC-FINDING:` lines, exactly like Pierre deep-review's `REVIEW-*`
|
|
21
|
+
lines. It is the free/local alternative to a paid cloud device farm — a pre-submit local
|
|
22
|
+
smoke signal, **not** a TestFlight / crash-reporter / QA replacement.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- **`skills/appstore-precheck/references/simulator-dynamic-review.md`** — the 6-check
|
|
26
|
+
agent-mode dynamic checklist (D1–D6) with advisory output format, modeled on the existing
|
|
27
|
+
deep-review reference docs.
|
|
28
|
+
- **`SKILL.md`** — an optional "Phase 6" section (off by default) plus a scoped READ-ONLY
|
|
29
|
+
caveat and an updated "Known limits" note.
|
|
30
|
+
- Methodology + README notes describing the tier.
|
|
31
|
+
|
|
32
|
+
### Notes
|
|
33
|
+
- Documentation-only: the offline scan path (`scan.sh`/`verdict.sh`/`bin/cli.js`/`action.yml`)
|
|
34
|
+
is byte-identical. READ-ONLY preserved — the tier touches only disposable simulator state,
|
|
35
|
+
never the user's repo. It is permanently local-only (macOS + Xcode + a simulator; it cannot
|
|
36
|
+
run in this project's `ubuntu-latest` CI). A deterministic `simulator-smoke.sh` and authored
|
|
37
|
+
Maestro flows are deliberately deferred (simulator crash-timing is too flaky to ship as an
|
|
38
|
+
unvalidated deterministic check).
|
|
39
|
+
|
|
40
|
+
## [1.11.0] - 2026-07-02
|
|
41
|
+
|
|
42
|
+
Roadmap #3: **rejection-outcome feedback loop** — a third, honestly-scoped measurement
|
|
43
|
+
axis. Reporting layer only; the scan and GREEN/YELLOW/RED verdict are untouched.
|
|
44
|
+
|
|
45
|
+
Alongside the synthetic and real-panel corpora, the tool now tracks **real App Store
|
|
46
|
+
review outcomes** (approved / rejected + Apple's cited guideline) in a committed,
|
|
47
|
+
human-reviewed ledger (`corpus/outcomes/ledger.json`, starts empty) and summarizes them
|
|
48
|
+
in `docs/scorecard.md`. The summary is **honesty-floored**: below 10 recorded outcomes
|
|
49
|
+
it shows only a raw tally and computes **no rate**; at/above the floor it may show a
|
|
50
|
+
directional recall estimate with a permanent survivorship-bias caveat. Because the
|
|
51
|
+
ledger is committed and read offline, the section is deterministic and covered by the
|
|
52
|
+
existing `scorecard.sh --check` — no network, no new CI job.
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
- **`corpus/outcomes/ledger.json`** (empty `[]`) + **`corpus/outcomes/README.md`** —
|
|
56
|
+
record schema, anonymization/privacy rules (no verbatim Apple text, no real identity),
|
|
57
|
+
contribution/review process, and the sample-size floor.
|
|
58
|
+
- **`scripts/scorecard-outcomes.sh`** — pure `bash`+`jq`, offline, deterministic; renders
|
|
59
|
+
the "Real App Store outcomes (n=N)" section with the honesty floor. Also
|
|
60
|
+
`scorecard.sh --outcomes`.
|
|
61
|
+
- The outcomes section is baked into `docs/scorecard.md`; Methodology now describes three
|
|
62
|
+
measurements.
|
|
63
|
+
- **`.github/ISSUE_TEMPLATE/app-store-outcome.md`** — anonymized outcome contribution that
|
|
64
|
+
funnels into a maintainer-reviewed PR.
|
|
65
|
+
- Test: `tests/test-scorecard-outcomes.sh`.
|
|
66
|
+
|
|
67
|
+
### Notes
|
|
68
|
+
- Reporting/measurement only — outcome data never influences the verdict or which rules
|
|
69
|
+
fire (a future, human-only decision). No verbatim Apple Resolution Center text is stored.
|
|
70
|
+
The scan path (`scan.sh`/`verdict.sh`/`bin/cli.js`/`action.yml`) is unchanged.
|
|
71
|
+
|
|
8
72
|
## [1.10.0] - 2026-07-02
|
|
9
73
|
|
|
10
74
|
Roadmap #4: **SARIF output + opt-in GitHub PR annotations** — read-only, no auto-fix.
|
package/README.md
CHANGED
|
@@ -468,8 +468,10 @@ CI runs ShellCheck, JSON validation, the version-consistency guard, and the fixt
|
|
|
468
468
|
|
|
469
469
|
This is a static heuristic tool plus Pierre's semantic deep review. A GREEN result **lowers but does
|
|
470
470
|
not eliminate** rejection risk; Apple's guidelines change frequently and reviewer decisions are
|
|
471
|
-
judgment calls. `REVIEW-FINDING` lines are advisory and do not block the token by themselves.
|
|
472
|
-
performs no runtime crash testing;
|
|
471
|
+
judgment calls. `REVIEW-FINDING` lines are advisory and do not block the token by themselves. The
|
|
472
|
+
default flow performs no runtime crash testing; an optional, opt-in local simulator tier
|
|
473
|
+
(Maestro and `xcrun simctl`) adds a pre-submit smoke signal but is not a TestFlight / crash-reporter
|
|
474
|
+
/ QA replacement. Always do a final manual review before you submit. Not affiliated
|
|
473
475
|
with Apple.
|
|
474
476
|
|
|
475
477
|
## Star History
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appstore-precheck",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Read-only iOS App Store pre-submission check, packaged as a portable Agent Skill with native Claude Code, Cursor, and Codex plugins, plus an npx CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Berkay Turk (https://github.com/berkayturk)",
|
|
@@ -4,7 +4,7 @@ description: Read-only pre-submission check for an iOS app before App Store revi
|
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: Berkay Turk
|
|
7
|
-
version: 1.
|
|
7
|
+
version: 1.12.0
|
|
8
8
|
allowed-tools: Bash Read Grep Glob WebFetch
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -92,7 +92,7 @@ Rules:
|
|
|
92
92
|
**English** (Français + user lang, or just Français if they asked in French).
|
|
93
93
|
- Keep each one-liner short (one sentence). Vary wording each run; stay in Pierre's dry critic voice.
|
|
94
94
|
|
|
95
|
-
## Flow (
|
|
95
|
+
## Flow (Phases 0–5; plus an optional opt-in Phase 6)
|
|
96
96
|
|
|
97
97
|
### Phase 0: Live guideline drift check
|
|
98
98
|
|
|
@@ -314,9 +314,27 @@ narrative; verdict.sh just pins the threshold arithmetic. `REVIEW-FINDING` lines
|
|
|
314
314
|
7. Print the final manual checklist (see
|
|
315
315
|
[`references/methodology.md`](references/methodology.md#pre-submit-manual-checklist)).
|
|
316
316
|
|
|
317
|
+
### Phase 6: local dynamic simulator tier (optional, opt-in — off by default)
|
|
318
|
+
|
|
319
|
+
**This phase does not run by default.** Run it ONLY when the user explicitly asks for a dynamic /
|
|
320
|
+
simulator check AND supplies a built app (a simulator `.app` path, or a booted simulator UDID +
|
|
321
|
+
bundle id). It uses `xcrun simctl` + Maestro MCP tools (`mcp__maestro__*`) to launch the app on a
|
|
322
|
+
disposable simulator and observe real behavior — the free/local alternative to a paid cloud device
|
|
323
|
+
farm.
|
|
324
|
+
|
|
325
|
+
It emits advisory `DYNAMIC-PASS:` / `DYNAMIC-FINDING:` lines and **never changes the
|
|
326
|
+
GREEN/YELLOW/RED verdict** (the verdict stays derived only from Phases 0–2). It is read-only w.r.t.
|
|
327
|
+
the user's project — it touches only disposable simulator state, never the repo. It requires macOS +
|
|
328
|
+
Xcode + a simulator runtime and is permanently local-only (it cannot run in CI). It is a pre-submit
|
|
329
|
+
local smoke signal, not a TestFlight / crash-reporter / QA replacement.
|
|
330
|
+
|
|
331
|
+
Follow [`references/simulator-dynamic-review.md`](references/simulator-dynamic-review.md) for the
|
|
332
|
+
6-check dynamic checklist and output format.
|
|
333
|
+
|
|
317
334
|
## Rules
|
|
318
335
|
|
|
319
|
-
- **READ-ONLY:** never change code or assets. Only report and write the token.
|
|
336
|
+
- **READ-ONLY:** never change code or assets. Only report and write the token. (The optional Phase 6
|
|
337
|
+
simulator tier touches disposable simulator state only — never the user's project.)
|
|
320
338
|
- **Speed > exhaustiveness:** `scan.sh` uses parallel grep/jq and finishes in seconds.
|
|
321
339
|
- **No error swallowing:** if any scan command fails, that line is reported as FAIL and the scan continues.
|
|
322
340
|
- **Token location:** `.precheck-pass` at the repo root; the guard tests it with an `mmin -60` filter.
|
|
@@ -324,7 +342,9 @@ narrative; verdict.sh just pins the threshold arithmetic. `REVIEW-FINDING` lines
|
|
|
324
342
|
|
|
325
343
|
## Known limits
|
|
326
344
|
|
|
327
|
-
-
|
|
345
|
+
- The default flow is static analysis only. Runtime crash/behavior testing is available as an
|
|
346
|
+
optional, opt-in local simulator tier (Phase 6, `references/simulator-dynamic-review.md`); it is a
|
|
347
|
+
pre-submit local smoke signal, not a TestFlight / crash-reporter replacement.
|
|
328
348
|
- Several checks are advisory WARNs gated on detected signals (Sign in with Apple 4.8,
|
|
329
349
|
external-purchase 3.1.1(a), tracking/IDFA without ATT, analytics vs privacy manifest, metadata
|
|
330
350
|
URLs and placeholder copy). The export-compliance key is flagged when absent, but the actual
|
|
@@ -12,6 +12,7 @@ whole file to run the skill.
|
|
|
12
12
|
- [Auto-detection rules](#auto-detection-rules)
|
|
13
13
|
- [Verdict thresholds](#verdict-thresholds)
|
|
14
14
|
- [SARIF output](#sarif-output---format-sarif)
|
|
15
|
+
- [Real App Store outcomes](#real-app-store-outcomes-corpusoutcomes)
|
|
15
16
|
- [Pre-submit manual checklist](#pre-submit-manual-checklist)
|
|
16
17
|
|
|
17
18
|
---
|
|
@@ -234,6 +235,30 @@ emits inline `::error`/`::warning` annotations, both opt-in.
|
|
|
234
235
|
|
|
235
236
|
---
|
|
236
237
|
|
|
238
|
+
### Real App Store outcomes (`corpus/outcomes/`)
|
|
239
|
+
|
|
240
|
+
Beyond the synthetic and real-panel corpora, the tool tracks real Apple review outcomes (approved /
|
|
241
|
+
rejected + cited guideline) in a committed, human-reviewed ledger (`corpus/outcomes/ledger.json`),
|
|
242
|
+
summarized in `docs/scorecard.md` by `scripts/scorecard-outcomes.sh`. It is honesty-floored: no rate
|
|
243
|
+
is computed below 10 records (raw tally only), and a permanent survivorship-bias caveat applies once
|
|
244
|
+
a rate is shown. It is a reporting layer only — it never influences the GREEN/YELLOW/RED verdict or
|
|
245
|
+
which rules fire. Contribute an outcome via the "App Store outcome" issue template.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
### Optional local dynamic simulator tier
|
|
250
|
+
|
|
251
|
+
Beyond the static scan and the agent-mode deep reviews, an **opt-in** local dynamic tier
|
|
252
|
+
(`references/simulator-dynamic-review.md`, SKILL.md Phase 6) can run the app on a simulator
|
|
253
|
+
(`xcrun simctl` + Maestro MCP) and emit advisory `DYNAMIC-PASS:` / `DYNAMIC-FINDING:` observations —
|
|
254
|
+
launch/crash, paywall render, permission prompt vs purpose string, demo-login path, live UI vs
|
|
255
|
+
marketing screenshots. It is off by default, never changes the verdict, is read-only w.r.t. the
|
|
256
|
+
user's project (touches disposable simulator state only), and is permanently local-only (it cannot
|
|
257
|
+
run in CI). It is a pre-submit local smoke signal, not a TestFlight / crash-reporter / QA
|
|
258
|
+
replacement.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
237
262
|
## Pre-submit manual checklist
|
|
238
263
|
|
|
239
264
|
Things the scanner cannot verify; confirm by hand before you submit:
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Local dynamic simulator tier (agent-mode, opt-in, non-blocking)
|
|
2
|
+
|
|
3
|
+
The fourth, opt-in tier: a **local dynamic** review that runs the app on a simulator and observes
|
|
4
|
+
real behavior — the class of checks a static scan cannot make (does it launch without crashing, does
|
|
5
|
+
the paywall actually render, does a permission prompt match its purpose string, does a demo login
|
|
6
|
+
work, does the live UI match the marketing screenshots). It is the free/local alternative to a paid
|
|
7
|
+
cloud device farm, built from `xcrun simctl` + Maestro MCP tools (`mcp__maestro__*`) already
|
|
8
|
+
available on a Mac with Xcode.
|
|
9
|
+
|
|
10
|
+
**Identity:** runs ONLY in agent-skill mode, ONLY when the user explicitly asks for it and supplies a
|
|
11
|
+
built app (or a booted simulator UDID + bundle id). It is NOT part of the offline CLI / npx /
|
|
12
|
+
GitHub-Action scan, and it never runs by default. Requires macOS + Xcode + a simulator runtime; it
|
|
13
|
+
cannot run in this project's `ubuntu-latest` CI and is permanently local-only.
|
|
14
|
+
|
|
15
|
+
**This tier never changes the verdict.** GREEN/YELLOW/RED comes only from the static scan counts
|
|
16
|
+
(Phases 0–2). This tier emits advisory `DYNAMIC-PASS:` / `DYNAMIC-FINDING:` lines, like Pierre
|
|
17
|
+
deep-review's `REVIEW-*` lines.
|
|
18
|
+
|
|
19
|
+
**Read-only:** read-only w.r.t. the user's project — the tier installs/launches onto a **disposable
|
|
20
|
+
simulator** only and never modifies the user's repo. Prefer a dedicated/disposable simulator device;
|
|
21
|
+
do not disturb the user's everyday simulator state; leave teardown to the user unless the tier
|
|
22
|
+
created the device itself.
|
|
23
|
+
|
|
24
|
+
**Not a guarantee:** a pre-submit local smoke signal — NOT a replacement for TestFlight, a crash
|
|
25
|
+
reporter, or real-device QA.
|
|
26
|
+
|
|
27
|
+
## Rules
|
|
28
|
+
|
|
29
|
+
- Advisory only: every check reports `DYNAMIC-PASS:` or `DYNAMIC-FINDING:` — never `FAIL`, never a
|
|
30
|
+
verdict contribution.
|
|
31
|
+
- Evidence-based: cite a screenshot filename + what was observed. If a check cannot be driven (no
|
|
32
|
+
paywall in this app, a UI selector can't be found, no permissions declared), report
|
|
33
|
+
`DYNAMIC-PASS: … — not applicable` or `DYNAMIC-FINDING: … could not verify` — never invent a
|
|
34
|
+
failure.
|
|
35
|
+
- All 6 checks, every run: report each as PASS / FINDING / not-applicable.
|
|
36
|
+
- Boot a disposable simulator, launch the supplied app, and tear down cleanly.
|
|
37
|
+
- Write Pierre's 2–3 sentence explanations in the user's conversation language.
|
|
38
|
+
|
|
39
|
+
## The 6 checks
|
|
40
|
+
|
|
41
|
+
| # | Guideline | Dynamic question |
|
|
42
|
+
|---|-----------|------------------|
|
|
43
|
+
| D1 | **2.1** | Does the app launch on a fresh simulator and stay foregrounded briefly without crashing / a crash alert? |
|
|
44
|
+
| D2 | **2.1** | Does it reach a real first screen (not stuck on a splash, a blank screen, or an error alert)? |
|
|
45
|
+
| D3 | **3.1.2** | If a paywall/subscription exists, does it render with price + trial/auto-renew/terms visible on-screen (not just present as strings)? |
|
|
46
|
+
| D4 | **5.1.1(ii)/(iii)** | For each permission with an `Info.plist` purpose string, does the OS prompt appear at the right moment and match the declared string? |
|
|
47
|
+
| D5 | **2.1** | For a login-gated app, is there a reachable guest/demo path, or do the declared review demo credentials actually log in? |
|
|
48
|
+
| D6 | **2.3.5** | Do live screenshots of key screens match the submitted marketing screenshots (features shown match the running build)? |
|
|
49
|
+
|
|
50
|
+
## Per-check procedure
|
|
51
|
+
|
|
52
|
+
### D1 — Launch without crash
|
|
53
|
+
1. `mcp__maestro__start_device` (or `xcrun simctl boot`) a disposable simulator; `launch_app` the supplied app/bundle id.
|
|
54
|
+
2. Observe for a short window; if the app process disappears or a crash alert shows, `DYNAMIC-FINDING`.
|
|
55
|
+
3. `take_screenshot` at launch as evidence.
|
|
56
|
+
|
|
57
|
+
### D2 — Core screen reachable
|
|
58
|
+
1. After launch, inspect the first real screen (`inspect_view_hierarchy` / `take_screenshot`).
|
|
59
|
+
2. Flag if stuck on a splash, blank, or an error/"something went wrong" state.
|
|
60
|
+
|
|
61
|
+
### D3 — Paywall renders
|
|
62
|
+
1. If a paywall exists (per the static scan / app structure), navigate to it (`tap_on` / a flow).
|
|
63
|
+
2. Confirm price + trial/auto-renew/cancel terms are visibly rendered; screenshot. Not applicable if no paywall.
|
|
64
|
+
|
|
65
|
+
### D4 — Permission prompt vs purpose string
|
|
66
|
+
1. For each `NS*UsageDescription` in `Info.plist`, trigger the feature that requests it.
|
|
67
|
+
2. Confirm the OS prompt appears at the right moment and its text matches the declared purpose string; screenshot.
|
|
68
|
+
|
|
69
|
+
### D5 — Demo / login path
|
|
70
|
+
1. For a login-gated app, look for a guest/demo entry, or enter the declared review demo credentials.
|
|
71
|
+
2. Confirm a reachable path to core features; `DYNAMIC-FINDING` if the only path is a wall with no working demo.
|
|
72
|
+
|
|
73
|
+
### D6 — Live UI vs marketing screenshots
|
|
74
|
+
1. Capture live screenshots of key screens.
|
|
75
|
+
2. Compare to the submitted marketing screenshots; flag features shown in marketing but absent in the running build.
|
|
76
|
+
|
|
77
|
+
## Output format
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
DYNAMIC-PASS: <guideline> — <one-line why it looks OK, with screenshot filename>
|
|
81
|
+
```
|
|
82
|
+
or
|
|
83
|
+
```
|
|
84
|
+
DYNAMIC-FINDING: <guideline> — <one-line concrete issue, with screenshot filename>
|
|
85
|
+
Pierre: <2–3 sentences: why Apple cares, what you saw, what to fix or verify>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
End with a summary table: 6 checks → count of `DYNAMIC-FINDING` vs `DYNAMIC-PASS` (note any not-applicable).
|