arcane-cli 0.26.1 โ†’ 0.27.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/README.md CHANGED
@@ -30,7 +30,7 @@ npm install -g arcane-cli # then: spell init
30
30
 
31
31
  <div align="center">
32
32
 
33
- **๐Ÿ” opinionated lifecycle** &nbsp;ยท&nbsp; **๐Ÿ“œ 36 spells** &nbsp;ยท&nbsp; **๐Ÿค– 12 agents** &nbsp;ยท&nbsp; **โš–๏ธ 23 governance standards** &nbsp;ยท&nbsp; **๐Ÿ“ markdown-native** &nbsp;ยท&nbsp; **๐Ÿ”Œ any AI client / tracker**
33
+ **๐Ÿ” opinionated lifecycle** &nbsp;ยท&nbsp; **๐Ÿ“œ 38 spells** &nbsp;ยท&nbsp; **๐Ÿค– 12 agents** &nbsp;ยท&nbsp; **โš–๏ธ 23 governance standards** &nbsp;ยท&nbsp; **๐Ÿ“ markdown-native** &nbsp;ยท&nbsp; **๐Ÿ”Œ any AI client / tracker**
34
34
 
35
35
  </div>
36
36
 
@@ -113,22 +113,27 @@ Arcane isn't a prompt snippet โ€” it's a full framework. Everything installs int
113
113
 
114
114
  | Layer | What you get |
115
115
  | --- | --- |
116
- | ๐Ÿ“œ **Spells** | **34** prompt-driven workflows spanning the entire lifecycle โ€” planning, architecture, implementation, testing, review, shipping, session management, and ops. |
117
- | โš–๏ธ **Governance** | **19** battle-tested standards as drop-in templates: git conventions, testing standards, CI/CD, threat model, ADR format, naming, security hardening, and more. |
116
+ | ๐Ÿ“œ **Spells** | **38** prompt-driven workflows spanning the entire lifecycle โ€” planning, architecture, implementation, testing, review, shipping, session management, and ops. |
117
+ | โš–๏ธ **Governance** | **23** battle-tested standards as drop-in templates: git conventions, testing standards, CI/CD, threat model, ADR format, naming, security hardening, and more. |
118
118
  | ๐Ÿค– **Agents** | **12** ready-made agent personas with roles, clusters, and a gamified autonomy model โ€” assign work and power levels per repo. |
119
119
  | ๐Ÿ› ๏ธ **CLI** | `spell init / add / update / status / uninstall` โ€” install by profile or ร  la carte, and keep everything in sync as new versions ship. |
120
120
 
121
+ <!-- arcane:start -->
121
122
  <details>
122
- <summary><b>๐Ÿ“œ The full spell catalogue (34)</b></summary>
123
-
124
- **Core Spell Loop** โ€” `plan` ยท `enchant` ยท `scope` ยท `architect` ยท `implement` ยท `test` ยท `review` ยท `ship`
125
- **Session** โ€” `open-session` ยท `close-session` ยท `status`
126
- **Operational & Git** โ€” `commit-work` ยท `todo` ยท `check-drift` ยท `bug` ยท `create-pull-request` ยท `address-review` ยท `bump`
127
- **Specialized** โ€” `full-cycle` ยท `review-batch` ยท `security-review` ยท `dotnet-expert` ยท `product-review` ยท `suggest-feature`
128
- **Knowledge & Docs** โ€” `document` ยท `explain-concept` ยท `feedback` ยท `save-idea` ยท `manifest`
129
- **Business & Admin** โ€” `summon-venture` ยท `brainstorm` ยท `generate-bot-icons` ยท `present-arcane` ยท `arcane-version`
123
+ <summary><b>๐Ÿ“œ The full spell catalogue (39)</b></summary>
124
+
125
+ **Session** โ€” `open-session` ยท `close-session` ยท `commit-work` ยท `status` ยท `arcane-version`
126
+ **Capture** โ€” `save-idea` ยท `todo` ยท `feedback` ยท `suggest-feature` ยท `document` ยท `brainstorm` ยท `explain-concept`
127
+ **Delivery** โ€” `create-pull-request` ยท `sync-pull-request` ยท `address-review`
128
+ **Review** โ€” `review` ยท `review-batch`
129
+ **Planning** โ€” `plan` ยท `architect` ยท `scope` ยท `product-review`
130
+ **Build** โ€” `implement` ยท `test` ยท `full-cycle` ยท `bug` ยท `bump` ยท `dotnet-expert` ยท `security-review` ยท `ship` ยท `enchant` ยท `generate-bot-icons` ยท `make-discoverable` ยท `scry` ยท `eas-store-deploy`
131
+ **Docs** โ€” `adopt-docs`
132
+ **Venture** โ€” `summon-venture` ยท `manifest`
133
+ **Meta** โ€” `present-arcane` ยท `check-drift`
130
134
 
131
135
  </details>
136
+ <!-- arcane:end -->
132
137
 
133
138
  <details>
134
139
  <summary><b>โš–๏ธ The governance standards (23)</b></summary>
@@ -0,0 +1,239 @@
1
+ ---
2
+ title: Mobile Release Standards
3
+ audience: both
4
+ last_updated: YYYY-MM-DD
5
+ status: active
6
+ distributable: true
7
+ tags: [mobile, app-store, google-play, release, deployment]
8
+ ---
9
+
10
+ # Mobile Release Standards
11
+
12
+ Durable facts about how the Apple App Store and Google Play review consoles and release pipelines
13
+ actually behave โ€” true regardless of which build tool produced the binary. These are platform
14
+ properties, not Expo/EAS-specific tooling behavior; the EAS-specific workflow that consumes these rules
15
+ lives in `spell-eas-store-deploy`. Sourced from two real dogfooding runs (a first-ever App Store
16
+ submission, then a from-zero Google Play listing to a live production release) โ€” every rule here was hit
17
+ for real, not derived from documentation alone.
18
+
19
+ ## Executive Summary
20
+
21
+ - A store's review/track model is not symmetric with the other store's, and approval on one track never
22
+ promotes a binary anywhere else โ€” treat each track and each store as its own release, not a step in a
23
+ shared pipeline.
24
+ - Several consoles distinguish "saved" from "submitted," and several silently drop scripted input โ€”
25
+ verify the console's own state readout before trusting that an action took effect (`EV-01`/`EV-03`).
26
+ - Identity and signing facts (the certificate an installed app actually presents, which `(package,
27
+ SHA-1)` pair an integration is keyed to, where a public developer address actually comes from) are
28
+ frequently not visible on the console page you'd naturally check โ€” read them from the artifact or the
29
+ system that actually enforces them, not from a console summary.
30
+ - Two platform-specific binary/UI constraints (notification icon requirements, a native dialog's button
31
+ cap) have no equivalent on the other platform and are easy to carry over incorrectly from iOS-first
32
+ development.
33
+
34
+ ---
35
+
36
+ ## Rule index
37
+
38
+ | ID | Rule (one line) |
39
+ |----|------|
40
+ | MR-01 | Google Play's tracks are independent: only *internal testing* skips review, and approval on any one track never promotes the binary to another. |
41
+ | MR-02 | A fresh Google Play production track starts with zero countries/regions, which surfaces as a generic, uninformative release error. |
42
+ | MR-03 | Google Play console edits are staged until an explicit "send for review" action; saving is not submitting. |
43
+ | MR-04 | Google Play's IARC content-rating questionnaire re-renders when the app category changes, shifting rows under answers already given. |
44
+ | MR-05 | "Data collected" means data that leaves the device โ€” and both stores' privacy/data-safety declarations must agree with each other. |
45
+ | MR-06 | The two stores' supported locale catalogues differ; do not copy one store's locale list onto the other. |
46
+ | MR-07 | Reviewer/demo-account access is scoped per app, not per account type, and is length-capped. |
47
+ | MR-08 | Google Play's "no deobfuscation file" warning is benign and permanent until a mapping file is uploaded. |
48
+ | MR-09 | The signing certificate an installed Android app actually presents can differ from what any single console page shows โ€” read it from the device, never infer it. |
49
+ | MR-10 | Android OAuth clients are keyed to one `(package name, SHA-1)` pair each, and ship with the custom-URI-scheme toggle off by default. |
50
+ | MR-11 | Google Play sources the public developer address from the D-U-N-S record; confirming it in-console locks in whatever address is currently on file. |
51
+ | MR-12 | Audit the permissions the built artifact actually declares, not what the source code appears to use. |
52
+ | MR-13 | Android push-notification icons need a dedicated monochrome asset shipped in a native build; there is no OTA path for this. |
53
+ | MR-14 | Android's native `AlertDialog` silently caps at three buttons; a menu built on `Alert.alert` past that cap loses the overflow, typically the dismiss action. |
54
+
55
+ ---
56
+
57
+ ## Release tracks and console state
58
+
59
+ ### MR-01: Google Play's tracks are independent โ€” nothing promotes automatically
60
+
61
+ *Internal testing* publishes with no review. *Closed*, *open*, and *production* each trigger a real
62
+ review. Passing review on one track says nothing about any other track: production is a wholly separate
63
+ release, with its own binary assignment and its own country list. An operator who watches a closed-test
64
+ release turn green will reasonably conclude the app is live to the public โ€” it is not, and nothing in
65
+ the console actively corrects that assumption.
66
+
67
+ **Rule (MR-01):** Treat each Google Play track as an independent release requiring its own review and
68
+ its own configuration; passing review on one track never promotes the binary to another.
69
+
70
+ ### MR-02: A fresh production track starts with zero countries โ€” and fails silently about why
71
+
72
+ Countries and regions are configured per track, not once per app. A brand-new production track therefore
73
+ starts with none selected. The release wizard responds to this with a generic "We found some problems
74
+ with your release / To save, fix errors" that never names the actual cause. The real signal is the track
75
+ summary reading `0 countries / regions` โ€” set the country list on the track before attempting to build
76
+ the release, not after the generic error appears.
77
+
78
+ **Rule (MR-02):** Before building a release for any Google Play track, confirm its country/region list
79
+ is non-empty; a generic "fix errors" message with no named cause is this failure's signature.
80
+
81
+ ### MR-03: Saving is not submitting
82
+
83
+ Google Play console edits land in the Publishing overview as staged changes. They reach Google only
84
+ through an explicit "Send changes for review" action, which itself carries a second confirmation dialog.
85
+ A workflow that stops at "Save" ships nothing. The listing pages carry the identical trap under a
86
+ differently-worded "Save and publish" action with its own second dialog.
87
+
88
+ **Rule (MR-03):** After any Google Play console edit, look for and complete the explicit send-for-review
89
+ (or save-and-publish) action and its confirmation dialog โ€” "saved" is not a release state.
90
+
91
+ ---
92
+
93
+ ## Console review quirks
94
+
95
+ ### MR-04: The IARC questionnaire re-renders on category change
96
+
97
+ Changing an app's category in the Google Play console causes the IARC content-rating questionnaire to
98
+ re-render, shifting which question sits at which row. A batch of clicks issued against the prior layout
99
+ lands on the wrong questions and can silently set an unintended answer โ€” including sensitive categories
100
+ like graphic violence or location sharing.
101
+
102
+ **Rule (MR-04):** Answer the IARC questionnaire one question at a time, and re-read the full form after
103
+ any change to the app's category, rather than assuming the row order is stable.
104
+
105
+ ### MR-08: The "no deobfuscation file" warning is benign and permanent
106
+
107
+ Google Play shows a "no deobfuscation file" warning on every release once mapping-file upload isn't
108
+ wired up. This warning does not resolve itself and does not indicate a build defect โ€” it will appear on
109
+ every future release until a mapping file is actually uploaded. State this explicitly wherever it
110
+ appears in documentation or in-flight guidance, since it reads as alarming on a first submission.
111
+
112
+ **Rule (MR-08):** Treat Google Play's "no deobfuscation file" warning as an expected, permanent state
113
+ absent mapping-file upload โ€” not a signal of a broken release.
114
+
115
+ ---
116
+
117
+ ## Cross-store consistency
118
+
119
+ ### MR-05: "Collected" means leaves the device โ€” and the two stores' answers must agree
120
+
121
+ Google Play's data-safety framework and Apple's App Privacy responses both hinge on the same underlying
122
+ definition: data is "collected" only if it leaves the device. Purely on-device processing is not
123
+ collection. Because both declarations are public, a reviewer or a user can compare them directly โ€” the
124
+ two stores' answers for the same app must describe the same underlying behavior, not merely satisfy each
125
+ store's form independently.
126
+
127
+ **Rule (MR-05):** Data-safety and privacy declarations across both stores must agree with each other on
128
+ what is actually collected, using "leaves the device" as the collection test in both places.
129
+
130
+ ### MR-06: The stores' locale catalogues differ
131
+
132
+ Release notes are supplied per-language, and the set of locales each store's catalogue supports is not
133
+ identical between Apple and Google. A locale available on one store may not exist on the other, so a
134
+ default-language or locale-coverage choice has to be made against each store's own list โ€” never copied
135
+ across from the other store's configuration.
136
+
137
+ **Rule (MR-06):** Resolve locale/language coverage against each store's own supported-locale list
138
+ independently; do not assume the other store's locale set applies.
139
+
140
+ ---
141
+
142
+ ## Access, identity, and signing
143
+
144
+ ### MR-07: Reviewer access is scoped per app and length-capped
145
+
146
+ The demo-credentials/reviewer-access box is required per app, not per account type, and has a length
147
+ cap. An app with genuinely distinct account types (consumer vs. business, for example) needs one working
148
+ account supplied directly, with prose describing how a reviewer reaches the other flows โ€” there is no
149
+ mechanism for supplying multiple credential sets natively.
150
+
151
+ **Rule (MR-07):** Provide one working reviewer account per app submission, with any additional
152
+ account-type flows described in prose rather than as separate credentials.
153
+
154
+ ### MR-09: Read the actual signing certificate โ€” never infer it
155
+
156
+ Google Play App Signing, combined with "automatic protection," can result in an installed app presenting
157
+ a signing certificate that does not appear on any console page an operator would naturally check.
158
+ Anything keyed to a `(package, SHA-1)` pair โ€” Maps API keys, Google Sign-In, App Links / `assetlinks.json`
159
+ โ€” then fails specifically on Play-delivered installs while working normally on sideloaded builds. Zero
160
+ traffic in a provider's own usage metrics is not evidence the app isn't calling it (an auth failure can
161
+ occur before any billable request is logged), and a working browser-based OAuth flow proves nothing
162
+ about certificate matching, since that flow never checks certificates. This is the same discipline
163
+ `EV-01` states in general form โ€” re-read the actual persisted/served state, never trust the interface โ€”
164
+ applied to a case where the "interface" is a console page that may not even show the value in question.
165
+
166
+ **Rule (MR-09):** Never infer an Android signing SHA-1 from a console page; read the certificate the
167
+ installed app actually presents, via `adb logcat` against a Play-delivered install (`EV-01`).
168
+
169
+ ### MR-10: Android OAuth clients are scoped to one `(package, SHA-1)` pair, custom scheme off by default
170
+
171
+ Each Android OAuth client is keyed to exactly one `(package name, SHA-1)` combination โ€” a Play-signed
172
+ install, an upload-signed sideload, and a separate `.dev` build variant each need their own client, not a
173
+ shared one. Separately, new Android OAuth clients ship with "Enable custom URI scheme" switched off by
174
+ default; leaving it off produces a visible `Error 400: invalid_request`, which is a console
175
+ configuration gap, not a code defect.
176
+
177
+ **Rule (MR-10):** Provision one Android OAuth client per distinct `(package, SHA-1)` pair, and confirm
178
+ "Enable custom URI scheme" is on for any client that needs a custom-scheme redirect.
179
+
180
+ ### MR-11: The public developer address comes from D-U-N-S, and confirming it locks it in
181
+
182
+ Google Play publishes the developer's address on the public listing, and that address is not directly
183
+ editable inside Play โ€” it is sourced from the developer's D-U-N-S record. The console offers only
184
+ "confirm" or "contact D&B." Clicking "confirm" first locks in whatever address is currently on file,
185
+ which for a solo operator is frequently a home address. The correct order is: fix the address at D&B
186
+ first, wait for propagation (days to weeks), let Google re-sync, and only then confirm. Android developer
187
+ verification carries its own hard deadline, so this correction needs to start early enough that the
188
+ D&B propagation window still fits before that deadline.
189
+
190
+ **Rule (MR-11):** Correct the address at the D-U-N-S source and allow it to propagate before confirming
191
+ it in the Google Play console โ€” confirming first permanently publishes whatever address is on file at
192
+ that moment.
193
+
194
+ ---
195
+
196
+ ## Binary and platform specifics
197
+
198
+ ### MR-12: Audit what the binary actually declares, not what the code appears to use
199
+
200
+ A dependency that is installed but no longer called from application code still contributes its declared
201
+ permissions to the built artifact, and every declared permission is both review and data-safety surface.
202
+ The only reliable check is against the artifact itself (`aapt2 dump permissions`, or an equivalent raw
203
+ manifest inspection) โ€” inspecting source code for call sites is not equivalent, since an unused
204
+ dependency's manifest entries survive independently of whether anything still calls it. Removing a
205
+ permission means removing the dependency that declares it, and the removal must be re-verified against a
206
+ newly built artifact, not assumed from the source diff.
207
+
208
+ **Rule (MR-12):** Verify declared permissions against the actually built artifact, not against source
209
+ code's apparent usage, both before submission and after removing a permission-declaring dependency.
210
+
211
+ ### MR-13: Android notification icons need a dedicated asset, shipped natively
212
+
213
+ Android requires a dedicated monochrome, white-on-transparent small icon for notifications; without one,
214
+ the system falls back to a generic glyph in the notification shade. This is a native configuration
215
+ change and therefore ships only in a build โ€” there is no OTA path for it. When diagnosing a "broken"
216
+ notification icon, check the notification shade specifically rather than the heads-up banner, since some
217
+ OEM skins legitimately omit the small icon from the banner view while still rendering it correctly in the
218
+ shade. The branded, full-color icon slot is `largeIcon`, a separate asset from the required small icon.
219
+
220
+ **Rule (MR-13):** Ship a dedicated monochrome small-icon asset for Android notifications in a native
221
+ build (never an OTA), and diagnose icon issues via the notification shade, not the heads-up banner.
222
+
223
+ ### MR-14: Android's native `AlertDialog` caps at three buttons
224
+
225
+ Android's native `AlertDialog` silently drops any button past a hard cap of three. A menu built on
226
+ `Alert.alert` with more than three options loses whatever falls past the cap โ€” in practice, typically
227
+ the *Cancel* row, leaving a dialog with no way to dismiss it. iOS has no equivalent limit
228
+ (`ActionSheetIOS` supports an arbitrary option count).
229
+
230
+ **Rule (MR-14):** For any Android menu that could exceed three options, use a real bottom sheet
231
+ dismissible at least three ways (an explicit cancel row, a scrim tap, and the hardware back gesture)
232
+ instead of `Alert.alert`.
233
+
234
+ ## Related
235
+
236
+ - [[.arcane/governance/external-verification-standards|External Verification Standards]] โ€” `EV-01`
237
+ (re-read persisted state) and `EV-03` (trust the readout, escalate rather than retry) are the general
238
+ principles `MR-09` applies to Android's signing-certificate opacity specifically.
239
+ - `spell-eas-store-deploy` โ€” the runnable EAS Build + EAS Submit workflow that cites these rules by ID.
@@ -54,6 +54,12 @@ Paste this entire file into any AI client (Claude web, ChatGPT, etc.) to give it
54
54
  - **Project/domain documents:** use explicit descriptive paths such as `docs/`, `security/`, `infrastructure/`, or a configured business root. These add project context alongside framework standards.
55
55
  - **Overrides are not yet supported:** editing a managed `.arcane/governance/` standard can be replaced by `arcane update`. Additive project documents are safe; overriding a shipped standard requires the open customization/override model.
56
56
 
57
+ ## Research Reports
58
+
59
+ Research findings (competitive analysis, technical spikes, feasibility studies โ€” the output of the Research & Backlog Analyst role) live at `docs/research/<topic-slug>.md`, one file per report. No dedicated spell produces these: `spell-document` already proposes a target path from project structure and matches its destination directory's existing conventions โ€” point it at `docs/research/` for investigative content, the same way it already proposes `docs/` or `.arcane/governance/` for other document types. The directory is created on first real report, not pre-seeded on `spell init`.
60
+
61
+ Use this repo's standard frontmatter (below) plus one addition: `sources` โ€” a list of what was consulted. Every claim in the body traces back to one of these or is explicitly marked speculative. Structure the body summary-first: a short **Summary** with the key findings, then **Findings** with full detail and inline citations, then an optional **Follow-ups** for anything that should become a TODO item โ€” route those via `spell-todo`, which cross-references the report path. Throughout, distinguish **verified facts**, **reasonable inferences**, and **speculation** explicitly; don't let an inference read as a checked fact.
62
+
57
63
  ## Documentation Format
58
64
 
59
65
  Every doc uses YAML frontmatter: `title`, `audience` (human/ai/both), `last_updated`, `status` (draft/active/deprecated), `tags`. Use wiki-links (`[[filename]]`) for cross-references.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Use PROACTIVELY when deploying an Expo app to the App Store or Google Play via EAS.
3
+ ---
4
+
5
+ # EAS Store Deploy
6
+
7
+ Invoke the Arcane `spell-eas-store-deploy` spell workflow.
8
+
9
+ See the full prompt at `.github/prompts/spell-eas-store-deploy.prompt.md` for the complete workflow definition.
10
+
11
+ ---
12
+
13
+ @.github/prompts/spell-eas-store-deploy.prompt.md
@@ -31,7 +31,7 @@ Workflow:
31
31
  - Decisions made (even implicit ones like naming choices, tool preferences, or workflow patterns).
32
32
  - Problems encountered and how they were resolved (troubleshooting).
33
33
  - Lessons learned (anything that cost time, surprised the user, or should prevent future mistakes).
34
- - **Capture tie-in tip:** If this session produced durable analysis worth formalizing (a design, an investigation, a reusable explanation), suggest running `spell-document` before closing so it lands in the right doc. For quick ideas worth keeping but not yet actionable, suggest `spell-save-idea`.
34
+ - **Capture tie-in tip:** If this session produced durable analysis worth formalizing (a design, an investigation, a research finding, a reusable explanation), suggest running `spell-document` before closing so it lands in the right doc โ€” a research finding defaults to `docs/research/<topic-slug>.md` per `portable-bootstrap.md`'s Research Reports convention. For quick ideas worth keeping but not yet actionable, suggest `spell-save-idea`.
35
35
 
36
36
  1b. **Verify in-flight async work before writing any output artifact (EF-21). Enforcement: structured spell gate (ARC-023) โ€” this workflow requires the observable state of every dispatched item before proceeding to step 2.** "I started it" is not "it worked." Before the journal, TODO.md, or the handoff are written, enumerate every piece of asynchronous work dispatched during this session that could still be running or could have failed after being kicked off โ€” CI/CD runs, deployments, package publishes, long-running background tasks, PR checks. For each, **actively check** its current status through whatever means is available (CI provider API/CLI, deployment dashboard, `gh run view`, `az pipelines runs show`, etc.) โ€” attempt the check before reaching for `unverifiable`; that state exists for genuine access limits (no CLI/API access, no credentials, provider outage), not for skipping a check that was merely inconvenient to run. Then classify into exactly one state:
37
37
 
@@ -25,8 +25,8 @@ The user's input describes what to capture and where. If no argument was provide
25
25
  Identify:
26
26
 
27
27
  - **Topics** โ€” what conversation insights to extract (e.g., "naming analysis", "architecture comparison").
28
- - **Target paths** โ€” where to write (e.g., `docs/...`, `.arcane/governance/...`). If the user gives only topics, propose sensible paths from the project structure.
29
- - **Document type** โ€” reference doc, decision record, analysis report, inventory, or guide.
28
+ - **Target paths** โ€” where to write (e.g., `docs/...`, `.arcane/governance/...`). If the user gives only topics, propose sensible paths from the project structure. For a research report specifically (cited investigation, competitive analysis, feasibility study), default to `docs/research/<topic-slug>.md` per `portable-bootstrap.md`'s Research Reports convention.
29
+ - **Document type** โ€” reference doc, decision record, analysis report, research report, inventory, or guide.
30
30
 
31
31
  ## Step 2 โ€” Extract from Conversation
32
32
 
@@ -0,0 +1,253 @@
1
+ ---
2
+ name: Spell โ€” EAS Store Deploy
3
+ description: Deploy an Expo app to the Apple App Store and Google Play via EAS Build + EAS Submit โ€” one-time setup, repeat deployments, and the known pitfalls hit dogfooding both flows for real.
4
+ argument-hint: Which store(s) to deploy to (ios, android, or both) and whether this is initial setup or a repeat deployment
5
+ agent: agent
6
+ ---
7
+
8
+ ## Executive Summary
9
+
10
+ - This spell documents the **EAS Build + EAS Submit** pipeline end to end for both the Apple App Store
11
+ and Google Play โ€” initial one-time setup, repeat deployments, and known pitfalls, in that order, for
12
+ each store.
13
+ - Roughly 80% of the material (credentials, `eas.json` profile shape, build-number handling, OTA rules)
14
+ is shared between the two stores; only the store-console work genuinely diverges. Covered once in a
15
+ shared preamble, then two store-specific sections.
16
+ - **Deliberately scoped to EAS Build + EAS Submit** โ€” no local Xcode, Xcode Cloud, or Fastlane coverage.
17
+ That scope is what keeps every step concrete and copy-pasteable.
18
+ - Every lesson here was hit for real across two dogfooding runs (a first-ever App Store submission, then
19
+ a from-zero Google Play listing to a live production release) โ€” none is general knowledge restated.
20
+ - Durable, store-specific platform facts are documented once in
21
+ [`mobile-release-standards.md`](../../.arcane/governance/mobile-release-standards.md) and cited here
22
+ by `MR-nn` ID; verification discipline is documented once in
23
+ [`external-verification-standards.md`](../../.arcane/governance/external-verification-standards.md)
24
+ and cited by `EV-nn` ID. This spell does not restate either.
25
+
26
+ ---
27
+
28
+ ## Placeholders
29
+
30
+ Every placeholder below follows the same resolution rule: pull it from `.arcane.json`, the invoking
31
+ frontmatter, or the repo's own existing `app.json`/`eas.json`; if it is not resolvable from any of those,
32
+ **ask** โ€” never assume. A wrong guess here produces a rejected submission or a build tied to the wrong
33
+ account, not a local mistake.
34
+
35
+ | Placeholder | What it is | Resolution rule |
36
+ | --- | --- | --- |
37
+ | `{IOS_BUNDLE_ID}` | The iOS bundle identifier. | Resolve from `app.json`'s `expo.ios.bundleIdentifier`; if unset, ask. |
38
+ | `{APPLE_TEAM_ID}` | The Apple Developer Team ID. | Resolve from `eas.json` or the Apple Developer account; if unset, ask. |
39
+ | `{ASC_APP_ID}` | The App Store Connect App ID (`ascAppId`). | Resolve from `eas.json`'s `submit.production.ios` block, or App Store Connect's own app record; if unset, ask. |
40
+ | `{APPLE_ID_EMAIL}` | The Apple ID email used to authenticate to App Store Connect. | Resolve from `eas.json`, or ask โ€” never assume which Apple ID owns the account. |
41
+ | `{EAS_PROJECT_SLUG}` | The EAS project ID/slug. | Resolve from `app.json`'s `expo.extra.eas.projectId` or `eas init` output; if unset, ask. |
42
+ | `{ANDROID_PACKAGE}` | The Android application ID / package name. | Resolve from `app.json`'s `expo.android.package`; if unset, ask. |
43
+ | `{ORG_NAME}` | The Expo/EAS organization name, if the project belongs to one. | Resolve from `eas.json` or the EAS dashboard; if not applicable, omit. |
44
+
45
+ ## Context files
46
+
47
+ - [governance/mobile-release-standards.md](../../.arcane/governance/mobile-release-standards.md) โ€”
48
+ every `MR-nn` rule cited below. Rules are **cited by ID only** in this spell, never restated. **If the
49
+ file is not installed**, this spell still runs in full โ€” findings just cite each check as `MR-nn
50
+ (rationale unavailable โ€” install the standards doc)` instead of linking to it.
51
+ - [governance/external-verification-standards.md](../../.arcane/governance/external-verification-standards.md) โ€”
52
+ every `EV-nn` rule cited below, cited by ID only. **If the file is not installed**, this spell still
53
+ runs, and still re-reads actual state rather than trusting a console's own success signal โ€” it just
54
+ cites those checks as `EV-nn (rationale unavailable โ€” install the standards doc)`.
55
+
56
+ ---
57
+
58
+ ## Shared EAS Preamble (both stores)
59
+
60
+ This section is common setup both stores' deployments depend on. Do it once.
61
+
62
+ ### `eas.json` profile structure
63
+
64
+ ```json
65
+ {
66
+ "build": {
67
+ "production": {
68
+ "autoIncrement": true
69
+ }
70
+ },
71
+ "submit": {
72
+ "production": {
73
+ "ios": {
74
+ "appleId": "{APPLE_ID_EMAIL}",
75
+ "ascAppId": "{ASC_APP_ID}",
76
+ "appleTeamId": "{APPLE_TEAM_ID}"
77
+ },
78
+ "android": {
79
+ "serviceAccountKeyPath": "./path/to/service-account.json",
80
+ "track": "production"
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ - `appVersionSource: "local"` (in `app.json`'s `expo` block) plus `production.autoIncrement: true` means
88
+ build numbers auto-increment on every build โ€” no manual version bump needed for a routine release.
89
+ - Credentials for both platforms can be **remote, EAS-managed** โ€” no local Mac or Xcode required for iOS
90
+ signing (Distribution Certificate, Provisioning Profile, Push Notifications key are all generated and
91
+ held by EAS), and no local Android keystore file required either.
92
+
93
+ ### Cross-platform lessons (apply to every release, either store)
94
+
95
+ - **Never ship a native rebuild while a submission is in review** just to carry a JS-only change โ€” it
96
+ can cost the queue position. Hold the change and ship it as an OTA update after the verdict lands.
97
+ - **A reviewer's first cold launch must already be correct.** A review build has to carry the work
98
+ natively; it cannot rely on an OTA update being fetched, because of the next rule.
99
+ - **OTA updates apply on the next launch, not the current one** โ€” the client downloads in the background
100
+ and swaps on relaunch. Do not diagnose a "stuck" update until after two full quit-and-reopen cycles.
101
+ - **๐Ÿ”ด Bundler transform caches do not key on `EXPO_PUBLIC_*` env vars.** On a persistent (self-hosted)
102
+ build runner, the cache survives between runs, so an update published to one channel can ship
103
+ another channel's inlined config โ€” for example, a production build silently pointing at a development
104
+ API. Pass `--clear-cache` on the publish lane, and verify by comparing the published `launchAsset`
105
+ hashes across channels: identical hashes for two channels that differ by at least one env var is proof
106
+ of the defect. **A green publish/build step is not evidence here (`EV-02`)** โ€” assert against the
107
+ actual published asset.
108
+ - **When a feature is removed or renamed, both stores' screenshots and descriptions are in the blast
109
+ radius.** Store metadata outlives the code it depicts, and it's the first thing a reviewer opens.
110
+
111
+ ---
112
+
113
+ ## Apple App Store
114
+
115
+ ### Initial one-time setup
116
+
117
+ 1. Confirm Apple Developer Program enrollment is active.
118
+ 2. Create the App Store Connect app record: name, `{IOS_BUNDLE_ID}`, SKU, and user access.
119
+ 3. Set the `eas.json` build + submit profile structure shown in the preamble above, filling in
120
+ `{APPLE_ID_EMAIL}`, `{ASC_APP_ID}`, and `{APPLE_TEAM_ID}`.
121
+ 4. Generate remote, EAS-managed credentials (Distribution Certificate, Provisioning Profile, Push
122
+ Notifications key) โ€” no local Mac or Xcode needed.
123
+ 5. Create an App Store Connect API key for non-interactive submits (choose EAS-managed vs.
124
+ manually-uploaded per the operator's preference).
125
+ 6. If App Store Connect already has this bundle ID linked and EAS's automatic capability sync fails
126
+ against it, toggle the affected capabilities manually in the Apple Developer console's Identifiers
127
+ page instead of retrying the automated sync.
128
+
129
+ ### Repeat deployments
130
+
131
+ ```bash
132
+ eas build --platform ios --profile production
133
+ eas submit --platform ios --profile production --latest
134
+ ```
135
+
136
+ Build numbers auto-increment per the preamble's `eas.json` config โ€” no manual version bump needed.
137
+ Realistic wait times: build ~10-20 minutes; Apple's binary processing ~5-10 minutes before the build is
138
+ visible in TestFlight. A build not yet visible immediately after `eas submit` reports success is
139
+ ordinarily processing lag, not a failed submission โ€” verify by checking TestFlight directly before
140
+ concluding otherwise (`EV-06`).
141
+
142
+ ### Known pitfalls
143
+
144
+ - **`Failed to patch capabilities: X ON, Y OFF`** โ€” happens when an App Store Connect app is already
145
+ linked to the bundle ID and Apple rejects EAS's automatic capability sync. Fix: `EXPO_NO_CAPABILITY_SYNC=1`
146
+ as a build-time env var, combined with the manual capability toggle from setup step 6.
147
+ - **`Command must be re-run to pick up new updates configuration`** โ€” EAS auto-installs `expo-updates`
148
+ mid-build the first time it's needed. Simply re-run the exact same `eas build` command; no
149
+ configuration change is required on your end.
150
+ - **A transient Apple "Internal Server Error" during App Store Connect API key creation** clears on a
151
+ simple retry โ€” no code or configuration change needed.
152
+
153
+ ---
154
+
155
+ ## Google Play
156
+
157
+ Read this before anything else: **passing review on one Google Play track never promotes the binary to
158
+ any other track** (`MR-01`) โ€” internal testing has no review at all, while closed, open, and production
159
+ each require one independently.
160
+
161
+ ### Initial one-time setup
162
+
163
+ 1. Create the Google Play Console app listing.
164
+ 2. **Set the country/region list on every track you plan to release to, before building that release** โ€”
165
+ a fresh production track starts with zero countries and fails with a generic, un-actionable error
166
+ otherwise (`MR-02`).
167
+ 3. Reuse the App Store's listing copy and screenshots as a starting point โ€” it keeps the two stores
168
+ consistent โ€” but re-review each asset against the actually shipped binary before publishing; a
169
+ screenshot advertising a since-removed feature is a common miss from blind reuse. Image dimensions
170
+ differ between stores, so assets need re-export, not re-invention.
171
+ 4. Set up a Google Play service account and reference its key from `eas.json`'s
172
+ `submit.production.android.serviceAccountKeyPath`.
173
+ 5. Complete the IARC content-rating questionnaire one question at a time, and re-read the full form
174
+ after any change to the app's category (`MR-04`).
175
+ 6. Complete the data-safety declaration using "leaves the device" as the collection test, and confirm it
176
+ agrees with the App Store's own App Privacy answers for the same app (`MR-05`).
177
+ 7. Provide reviewer/demo-account access โ€” one working account per app, described in prose for any
178
+ additional account types (`MR-07`).
179
+ 8. If Android developer verification is pending, correct the public developer address at its D-U-N-S
180
+ source and let it propagate **before** confirming it in the Play console โ€” confirming first locks in
181
+ whatever address is currently on file (`MR-11`). Start this early: verification has a hard deadline,
182
+ and D&B propagation can take days to weeks.
183
+
184
+ ### Repeat deployments
185
+
186
+ ```bash
187
+ eas build --platform android --profile production
188
+ eas submit --platform android --profile production --latest
189
+ ```
190
+
191
+ **Saving a console edit is not submitting it** โ€” Google Play stages edits until an explicit "send
192
+ changes for review" (or "save and publish") action, each with its own confirmation dialog (`MR-03`). A
193
+ workflow that stops at "Save" ships nothing.
194
+
195
+ Observed review latencies, for expectation-setting: internal testing is instant (no review); a first
196
+ closed-testing review is roughly one day; production review is quoted as "typically within 7 days," and
197
+ materially faster when the identical binary already cleared a closed-track review. Treat a release not
198
+ yet visible immediately after submission as this ordinary review latency, not a failure, absent a
199
+ console error stating otherwise (`EV-06`).
200
+
201
+ ### Known pitfalls
202
+
203
+ - **๐Ÿ”ด Play Console silently drops synthetic browser input.** Programmatic form input and coordinate
204
+ clicks frequently do not register โ€” the control appears set, the save succeeds, and nothing persists
205
+ (seen on both app-access contact fields and country checkboxes). Before saving, read the page's own
206
+ state readout (a "Targeted (N)" filter chip, "Release notes provided for N languages," a track's
207
+ country-count summary); if the number disagrees with intent, hand the step to a human rather than
208
+ retrying the same automated input (`EV-01`/`EV-03`).
209
+ - **The "no deobfuscation file" warning** appears on every release and is benign and permanent until
210
+ mapping-file upload is wired up โ€” it is not a sign of a broken build (`MR-08`).
211
+ - **๐Ÿ”ด The signing certificate an installed app presents can differ from every console page you'd
212
+ naturally check.** Play App Signing plus "automatic protection" can make anything keyed to a
213
+ `(package, SHA-1)` pair โ€” Maps, Google Sign-In, App Links/`assetlinks.json` โ€” fail on Play-delivered
214
+ installs while working on sideloads. Never infer the SHA-1; read it from `adb logcat` against an
215
+ actual Play-delivered install (`MR-09`, `EV-01`). Zero traffic in a provider's usage metrics is not
216
+ evidence the app isn't calling it โ€” an auth failure can occur before any billable request is logged โ€”
217
+ and a working browser-based OAuth flow proves nothing about certificate matching.
218
+ - **Android OAuth clients are keyed to one `(package, SHA-1)` pair each** โ€” a Play-signed install, an
219
+ upload-signed sideload, and a `.dev` variant each need their own client. New clients also ship with
220
+ "Enable custom URI scheme" off, which fails with a visible `Error 400: invalid_request` until toggled
221
+ on (`MR-10`).
222
+ - **Audit what the built artifact actually declares** (`aapt2 dump permissions`, or an equivalent
223
+ manifest inspection) before submission โ€” a dependency that's installed but no longer called still
224
+ contributes its permissions, and removing one means removing the dependency, re-verified against a
225
+ fresh build (`MR-12`).
226
+ - **Android notification icons need a dedicated monochrome, white-on-transparent small icon**, shipped
227
+ in a build โ€” there's no OTA path for this. A "broken" icon symptom should be checked in the
228
+ notification shade, not the heads-up banner, since some OEM skins legitimately omit the small icon
229
+ from banners only (`MR-13`).
230
+ - **Android's native `AlertDialog` caps at three buttons.** A menu built on `Alert.alert` past that cap
231
+ silently loses the overflow โ€” typically the *Cancel* row. Use a real bottom sheet instead for any menu
232
+ that could exceed three options (`MR-14`).
233
+ - **Locale lists differ between the stores.** Release notes are per-language; resolve locale coverage
234
+ against each store's own catalogue rather than copying the other store's list (`MR-06`).
235
+
236
+ ---
237
+
238
+ ## Rules
239
+
240
+ This spell defers entirely to
241
+ [governance/mobile-release-standards.md](../../.arcane/governance/mobile-release-standards.md) for what
242
+ each store-specific platform fact means and why, and to
243
+ [governance/external-verification-standards.md](../../.arcane/governance/external-verification-standards.md)
244
+ for how any claimed success is verified. Findings and steps in this spell cite rule IDs from both; they
245
+ do not re-explain them. Where the two disagree with what this spell says to do, the governance docs win.
246
+
247
+ ## Related
248
+
249
+ - **Consumes from:** whatever workflow builds and versions the app before deployment (in this
250
+ repository's own stack, `spell-implement`/`spell-ship`) โ€” this spell deploys an already-built app to
251
+ the stores, it does not build the app's features.
252
+ - [Mobile Release Standards](../../.arcane/governance/mobile-release-standards.md)
253
+ - [External Verification Standards](../../.arcane/governance/external-verification-standards.md)
@@ -118,6 +118,7 @@ Determine the target file(s) for each item:
118
118
  | Speculative idea or future-facing concept | `TODO.md` under `## Product Ideas` or the best matching section |
119
119
  | ADR candidate | `DECISIONS.md` โ€” flag it as pending, do not write the full ADR yet |
120
120
  | Journal-worthy context | `journal/` โ€” propose a filename with today's date slug, do not create the file yet |
121
+ | Finding sourced from a research report | `TODO.md` under the matching section โ€” cross-reference the report path (e.g., `โ€” see docs/research/<slug>.md`) |
121
122
  | Business-specific execution item | Both `TODO.md` AND the relevant `{BUSINESS_ROOT}/<name>/overview.md` reference section |
122
123
 
123
124
  ## Step 4 โ€” Show Proposal
package/dist/index.js CHANGED
@@ -290,7 +290,8 @@ var PROFILE_CONFIGS = [
290
290
  "universal-agent-rules",
291
291
  "spell-authoring-standards",
292
292
  "external-verification-standards",
293
- "web-discoverability-standards"
293
+ "web-discoverability-standards",
294
+ "mobile-release-standards"
294
295
  ]
295
296
  }
296
297
  ];
@@ -452,6 +453,11 @@ var COMPONENTS = [
452
453
  description: "Search-engine and AI-assistant discoverability rules for web properties",
453
454
  files: [".arcane/governance/web-discoverability-standards.md"]
454
455
  },
456
+ {
457
+ name: "mobile-release-standards",
458
+ description: "Durable App Store / Google Play console and release-track facts consumed by spell-eas-store-deploy",
459
+ files: [".arcane/governance/mobile-release-standards.md"]
460
+ },
455
461
  // โ”€โ”€โ”€ Spells โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
456
462
  // Split from the former monolithic `spell-prompts` + `claude-commands` pair
457
463
  // so a profile can select spells by capability (see ARC-0NN). Grouping lives
@@ -562,7 +568,9 @@ var COMPONENTS = [
562
568
  ".github/prompts/spell-make-discoverable.prompt.md",
563
569
  ".claude/commands/spell-make-discoverable.md",
564
570
  ".github/prompts/spell-scry.prompt.md",
565
- ".claude/commands/spell-scry.md"
571
+ ".claude/commands/spell-scry.md",
572
+ ".github/prompts/spell-eas-store-deploy.prompt.md",
573
+ ".claude/commands/spell-eas-store-deploy.md"
566
574
  ]
567
575
  },
568
576
  {