@evertonferreira96/agents 0.0.0-stage → 0.1.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.
Files changed (258) hide show
  1. package/.claude/agents/api.md +45 -0
  2. package/.claude/agents/app.md +41 -0
  3. package/.claude/agents/architect.md +49 -0
  4. package/.claude/agents/infra.md +27 -0
  5. package/.claude/agents/product.md +58 -0
  6. package/.claude/agents/quick.md +33 -0
  7. package/.claude/agents/security.md +39 -0
  8. package/.claude/agents/ui.md +54 -0
  9. package/.claude/agents/verifier.md +56 -0
  10. package/.claude/agents/web.md +41 -0
  11. package/.claude/hooks/config-update-check.mjs +14 -0
  12. package/.claude/hooks/context-budget.mjs +51 -0
  13. package/.claude/hooks/graphify-update.sh +16 -0
  14. package/.claude/hooks/guard-bash.sh +64 -0
  15. package/.claude/hooks/orchestrator-guard.sh +49 -0
  16. package/.claude/hooks/post-compact.sh +26 -0
  17. package/.claude/hooks/readonly-guard.sh +54 -0
  18. package/.claude/hooks/stop-verify.sh +52 -0
  19. package/.claude/rules/api.md +21 -0
  20. package/.claude/rules/db.md +9 -0
  21. package/.claude/rules/delivery-diagram.md +16 -0
  22. package/.claude/rules/git-workflow.md +34 -0
  23. package/.claude/rules/graphify.md +3 -0
  24. package/.claude/rules/mobile.md +22 -0
  25. package/.claude/rules/tickets.md +9 -0
  26. package/.claude/rules/ui.md +14 -0
  27. package/.claude/rules/web.md +27 -0
  28. package/.claude/settings.json +114 -0
  29. package/.claude/skills/animate/RECIPES.md +324 -0
  30. package/.claude/skills/animate/SKILL.md +208 -0
  31. package/.claude/skills/animate/agents/openai.yaml +5 -0
  32. package/.claude/skills/animate-expo/RECIPES.md +385 -0
  33. package/.claude/skills/animate-expo/SKILL.md +264 -0
  34. package/.claude/skills/animate-expo/agents/openai.yaml +5 -0
  35. package/.claude/skills/animation-performance/SKILL.md +223 -0
  36. package/.claude/skills/animation-performance/agents/openai.yaml +5 -0
  37. package/.claude/skills/apple-design/SKILL.md +291 -0
  38. package/.claude/skills/apple-design/agents/openai.yaml +5 -0
  39. package/.claude/skills/arch-api/SKILL.md +291 -0
  40. package/.claude/skills/arch-api/agents/openai.yaml +3 -0
  41. package/.claude/skills/arch-api/references/common.md +174 -0
  42. package/.claude/skills/arch-api/references/contract.md +289 -0
  43. package/.claude/skills/arch-api/references/domain.md +301 -0
  44. package/.claude/skills/arch-api/references/endpoints.md +184 -0
  45. package/.claude/skills/arch-api/references/review.md +228 -0
  46. package/.claude/skills/arch-api/references/security.md +151 -0
  47. package/.claude/skills/arch-app/SKILL.md +278 -0
  48. package/.claude/skills/arch-app/agents/openai.yaml +3 -0
  49. package/.claude/skills/arch-app/references/navigation-session.md +257 -0
  50. package/.claude/skills/arch-app/references/review.md +155 -0
  51. package/.claude/skills/arch-app/references/screens.md +214 -0
  52. package/.claude/skills/arch-app/references/services.md +301 -0
  53. package/.claude/skills/arch-app/references/ui-mobile.md +124 -0
  54. package/.claude/skills/arch-db/SKILL.md +181 -0
  55. package/.claude/skills/arch-db/agents/openai.yaml +3 -0
  56. package/.claude/skills/arch-db/references/indexes-queries.md +251 -0
  57. package/.claude/skills/arch-db/references/migrations.md +191 -0
  58. package/.claude/skills/arch-db/references/modeling.md +214 -0
  59. package/.claude/skills/arch-db/references/mysql.md +262 -0
  60. package/.claude/skills/arch-db/references/pooling-ops.md +112 -0
  61. package/.claude/skills/arch-db/references/review.md +143 -0
  62. package/.claude/skills/arch-db/references/transactions-locks.md +141 -0
  63. package/.claude/skills/arch-web/SKILL.md +307 -0
  64. package/.claude/skills/arch-web/agents/openai.yaml +3 -0
  65. package/.claude/skills/arch-web/references/auth-session-env.md +165 -0
  66. package/.claude/skills/arch-web/references/blocks.md +198 -0
  67. package/.claude/skills/arch-web/references/cache.md +124 -0
  68. package/.claude/skills/arch-web/references/review.md +142 -0
  69. package/.claude/skills/arch-web/references/services.md +297 -0
  70. package/.claude/skills/arch-web/references/tests.md +159 -0
  71. package/.claude/skills/arch-web/references/ui-forms.md +143 -0
  72. package/.claude/skills/break-ui/CATALOG.md +135 -0
  73. package/.claude/skills/break-ui/SKILL.md +176 -0
  74. package/.claude/skills/break-ui/agents/openai.yaml +5 -0
  75. package/.claude/skills/brief/SKILL.md +91 -0
  76. package/.claude/skills/brief/agents/openai.yaml +3 -0
  77. package/.claude/skills/diagnosing-bugs/SKILL.md +138 -0
  78. package/.claude/skills/diagnosing-bugs/agents/openai.yaml +3 -0
  79. package/.claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
  80. package/.claude/skills/emil-design-eng/SKILL.md +675 -0
  81. package/.claude/skills/emil-design-eng/agents/openai.yaml +5 -0
  82. package/.claude/skills/find-animation-opportunities/SKILL.md +141 -0
  83. package/.claude/skills/find-animation-opportunities/agents/openai.yaml +5 -0
  84. package/.claude/skills/graphify/.graphify_version +1 -0
  85. package/.claude/skills/graphify/SKILL.md +742 -0
  86. package/.claude/skills/graphify/agents/openai.yaml +3 -0
  87. package/.claude/skills/graphify/references/add-watch.md +56 -0
  88. package/.claude/skills/graphify/references/exports.md +91 -0
  89. package/.claude/skills/graphify/references/extraction-spec.md +70 -0
  90. package/.claude/skills/graphify/references/github-and-merge.md +46 -0
  91. package/.claude/skills/graphify/references/hooks.md +33 -0
  92. package/.claude/skills/graphify/references/query.md +317 -0
  93. package/.claude/skills/graphify/references/transcribe.md +52 -0
  94. package/.claude/skills/graphify/references/update.md +221 -0
  95. package/.claude/skills/grill-me/SKILL.md +7 -0
  96. package/.claude/skills/grill-me/agents/openai.yaml +5 -0
  97. package/.claude/skills/grill-with-docs/SKILL.md +64 -0
  98. package/.claude/skills/grill-with-docs/agents/openai.yaml +5 -0
  99. package/.claude/skills/grilling/SKILL.md +30 -0
  100. package/.claude/skills/grilling/agents/openai.yaml +3 -0
  101. package/.claude/skills/handoff/SKILL.md +28 -0
  102. package/.claude/skills/handoff/agents/openai.yaml +5 -0
  103. package/.claude/skills/impeccable/SKILL.md +89 -0
  104. package/.claude/skills/impeccable/agents/openai.yaml +5 -0
  105. package/.claude/skills/impeccable/reference/adapt.md +318 -0
  106. package/.claude/skills/impeccable/reference/adapt.native.md +58 -0
  107. package/.claude/skills/impeccable/reference/android.md +46 -0
  108. package/.claude/skills/impeccable/reference/animate.md +89 -0
  109. package/.claude/skills/impeccable/reference/audit.md +137 -0
  110. package/.claude/skills/impeccable/reference/audit.native.md +139 -0
  111. package/.claude/skills/impeccable/reference/bolder.md +33 -0
  112. package/.claude/skills/impeccable/reference/clarify.md +94 -0
  113. package/.claude/skills/impeccable/reference/colorize.md +86 -0
  114. package/.claude/skills/impeccable/reference/component-review.md +63 -0
  115. package/.claude/skills/impeccable/reference/craft-floor.md +44 -0
  116. package/.claude/skills/impeccable/reference/craft.md +5 -0
  117. package/.claude/skills/impeccable/reference/critique.md +809 -0
  118. package/.claude/skills/impeccable/reference/degraded/asset-producer.md +42 -0
  119. package/.claude/skills/impeccable/reference/degraded/documenter.md +24 -0
  120. package/.claude/skills/impeccable/reference/degraded/finish-reviewer.md +38 -0
  121. package/.claude/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  122. package/.claude/skills/impeccable/reference/delight.md +70 -0
  123. package/.claude/skills/impeccable/reference/distill.md +111 -0
  124. package/.claude/skills/impeccable/reference/doctor.md +54 -0
  125. package/.claude/skills/impeccable/reference/document.md +416 -0
  126. package/.claude/skills/impeccable/reference/extract.md +69 -0
  127. package/.claude/skills/impeccable/reference/generate.md +101 -0
  128. package/.claude/skills/impeccable/reference/harden.md +345 -0
  129. package/.claude/skills/impeccable/reference/hooks.md +113 -0
  130. package/.claude/skills/impeccable/reference/init.md +131 -0
  131. package/.claude/skills/impeccable/reference/ios.md +51 -0
  132. package/.claude/skills/impeccable/reference/layout.md +84 -0
  133. package/.claude/skills/impeccable/reference/live-setup.md +104 -0
  134. package/.claude/skills/impeccable/reference/live.md +325 -0
  135. package/.claude/skills/impeccable/reference/mode-operate.md +21 -0
  136. package/.claude/skills/impeccable/reference/mode-persuade.md +19 -0
  137. package/.claude/skills/impeccable/reference/mode-read.md +21 -0
  138. package/.claude/skills/impeccable/reference/new-work.md +154 -0
  139. package/.claude/skills/impeccable/reference/onboard.md +234 -0
  140. package/.claude/skills/impeccable/reference/operate.md +61 -0
  141. package/.claude/skills/impeccable/reference/optimize.md +258 -0
  142. package/.claude/skills/impeccable/reference/overdrive.md +127 -0
  143. package/.claude/skills/impeccable/reference/polish.md +105 -0
  144. package/.claude/skills/impeccable/reference/quieter.md +99 -0
  145. package/.claude/skills/impeccable/reference/region-map.md +26 -0
  146. package/.claude/skills/impeccable/reference/routing.md +24 -0
  147. package/.claude/skills/impeccable/reference/shape.md +59 -0
  148. package/.claude/skills/impeccable/reference/typeset.md +80 -0
  149. package/.claude/skills/impeccable/reference/visualize.md +47 -0
  150. package/.claude/skills/impeccable/scripts/VERSION +1 -0
  151. package/.claude/skills/impeccable/scripts/command-metadata.json +98 -0
  152. package/.claude/skills/impeccable/scripts/data/font-index-failures.json +121 -0
  153. package/.claude/skills/impeccable/scripts/data/font-index.json +1 -0
  154. package/.claude/skills/impeccable/scripts/impeccable +206 -0
  155. package/.claude/skills/impeccable/scripts/impeccable.cmd +214 -0
  156. package/.claude/skills/impeccable/scripts/live-browser-dom.js +281 -0
  157. package/.claude/skills/impeccable/scripts/live-browser-ignores.js +242 -0
  158. package/.claude/skills/impeccable/scripts/live-browser-session.js +148 -0
  159. package/.claude/skills/impeccable/scripts/live-browser.js +13532 -0
  160. package/.claude/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
  161. package/.claude/skills/improve-animations/AUDIT.md +115 -0
  162. package/.claude/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
  163. package/.claude/skills/improve-animations/SKILL.md +110 -0
  164. package/.claude/skills/improve-animations/agents/openai.yaml +5 -0
  165. package/.claude/skills/mobile-native/SKILL.md +311 -0
  166. package/.claude/skills/mobile-native/agents/openai.yaml +3 -0
  167. package/.claude/skills/orchestrate/SKILL.md +181 -0
  168. package/.claude/skills/orchestrate/agents/openai.yaml +3 -0
  169. package/.claude/skills/pick-ui-library/SKILL.md +85 -0
  170. package/.claude/skills/pick-ui-library/agents/openai.yaml +5 -0
  171. package/.claude/skills/ponytail/SKILL.md +52 -0
  172. package/.claude/skills/ponytail/agents/openai.yaml +3 -0
  173. package/.claude/skills/ponytail-audit/SKILL.md +103 -0
  174. package/.claude/skills/ponytail-audit/agents/openai.yaml +3 -0
  175. package/.claude/skills/ponytail-debt/SKILL.md +44 -0
  176. package/.claude/skills/ponytail-debt/agents/openai.yaml +3 -0
  177. package/.claude/skills/ponytail-review/SKILL.md +101 -0
  178. package/.claude/skills/ponytail-review/agents/openai.yaml +3 -0
  179. package/.claude/skills/postgres-best-practices/SKILL.md +42 -0
  180. package/.claude/skills/postgres-best-practices/agents/openai.yaml +3 -0
  181. package/.claude/skills/postgres-best-practices/references/backup-restore.md +372 -0
  182. package/.claude/skills/postgres-best-practices/references/bulk-loading.md +315 -0
  183. package/.claude/skills/postgres-best-practices/references/connection-pooling.md +310 -0
  184. package/.claude/skills/postgres-best-practices/references/hot-standby.md +221 -0
  185. package/.claude/skills/postgres-best-practices/references/indexing.md +318 -0
  186. package/.claude/skills/postgres-best-practices/references/logical-replication.md +610 -0
  187. package/.claude/skills/postgres-best-practices/references/major-version-upgrades.md +334 -0
  188. package/.claude/skills/postgres-best-practices/references/performance-diagnostics.md +553 -0
  189. package/.claude/skills/postgres-best-practices/references/query-optimization.md +294 -0
  190. package/.claude/skills/postgres-best-practices/references/query-patterns.md +566 -0
  191. package/.claude/skills/postgres-best-practices/references/schema-design.md +362 -0
  192. package/.claude/skills/postgres-best-practices/references/security-roles.md +489 -0
  193. package/.claude/skills/postgres-best-practices/references/transaction-isolation.md +344 -0
  194. package/.claude/skills/project-docs/SKILL.md +330 -0
  195. package/.claude/skills/project-docs/agents/openai.yaml +3 -0
  196. package/.claude/skills/prototype/PICKER.md +197 -0
  197. package/.claude/skills/prototype/SKILL.md +98 -0
  198. package/.claude/skills/prototype/agents/openai.yaml +5 -0
  199. package/.claude/skills/retro/SKILL.md +43 -0
  200. package/.claude/skills/retro/agents/openai.yaml +5 -0
  201. package/.claude/skills/review/SKILL.md +74 -0
  202. package/.claude/skills/review/agents/openai.yaml +3 -0
  203. package/.claude/skills/review/references/smells.md +23 -0
  204. package/.claude/skills/review-animations/SKILL.md +120 -0
  205. package/.claude/skills/review-animations/STANDARDS.md +187 -0
  206. package/.claude/skills/review-animations/agents/openai.yaml +5 -0
  207. package/.claude/skills/tdd/SKILL.md +38 -0
  208. package/.claude/skills/tdd/agents/openai.yaml +3 -0
  209. package/.claude/skills/tdd/mocking.md +59 -0
  210. package/.claude/skills/tdd/tests.md +77 -0
  211. package/.claude/skills/triage/SKILL.md +58 -0
  212. package/.claude/skills/triage/agents/openai.yaml +3 -0
  213. package/.claude/skills/ui-ux-pro-max/SKILL.md +378 -0
  214. package/.claude/skills/ui-ux-pro-max/agents/openai.yaml +5 -0
  215. package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
  216. package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
  217. package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
  218. package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
  219. package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
  220. package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  221. package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  222. package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  223. package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  224. package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  225. package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  226. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  227. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  228. package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  229. package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  230. package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  231. package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  232. package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  233. package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  234. package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
  235. package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
  236. package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  237. package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  238. package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  239. package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
  240. package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  241. package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
  242. package/.claude/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
  243. package/.claude/skills/writing-for-agents/SKILL.md +81 -0
  244. package/.claude/skills/writing-for-agents/agents/openai.yaml +3 -0
  245. package/.worktreeinclude +3 -0
  246. package/AGENTS.md +44 -0
  247. package/README.md +14 -2
  248. package/VERSION +1 -0
  249. package/bin/agents.mjs +45 -0
  250. package/docs/adr/README.md +31 -0
  251. package/docs/brief-template.md +3 -0
  252. package/docs/plans/README.md +73 -0
  253. package/package.json +46 -4
  254. package/scripts/bootstrap.mjs +188 -0
  255. package/scripts/check-catalog.mjs +43 -0
  256. package/scripts/skills-update.mjs +51 -0
  257. package/skills-lock.json +269 -0
  258. package/templates/CLAUDE.project.md +41 -0
@@ -0,0 +1,278 @@
1
+ ---
2
+ name: arch-app
3
+ description: "Implementa, revisa e refatora apps React Native Expo: boundaries navigation/screens/modules/common, grupos de tela em `src/screens/(public|private)/` com screen fina e `modules.tsx`, services como application layer que devolvem `ServiceResponse` e chamam o client OpenAPI gerado via request autenticado, entities derivadas do contrato, gateways só para fonte fora do OpenAPI, sessão em `expo-secure-store`, env `EXPO_PUBLIC_*` validado com Zod, observabilidade, tema/tokens e testes de regra pura. Use ao criar ou revisar telas, navigators, rotas e deep links, features em `src/modules`, services, auth/sessão, env ou testes de qualquer app Expo. Não use para APIs NestJS (`arch-api`) nem para o web Next.js (`arch-web`); visual e design system vão para `/ui`."
4
+ ---
5
+
6
+ # arch-app
7
+
8
+ Arquitetura de apps React Native Expo: telas agrupadas e finas, services que são o caso de uso e falam com o client OpenAPI gerado, sessão segura no dispositivo, env validado e observabilidade desde o primeiro service.
9
+
10
+ ```text
11
+ App compõe providers e o root navigator; navigator registra rotas.
12
+ Screen é fina: compõe `Module.*` do seu grupo e, quando orquestra, um controller hook.
13
+ Module interage: estado de UI, chama o service, trata `status`, dá feedback.
14
+ Service é o caso de uso: regra, mock, request autenticado, `ServiceResponse`.
15
+ Entities derivam do contrato gerado: a API é a fonte única do contrato.
16
+ `common` não conhece produto.
17
+ ```
18
+
19
+ Fluxo: `App -> root.navigator -> <nome>.screen -> Module.* -> service -> authedRequest -> api.<org>.<api>.client.<operationId> -> API`.
20
+
21
+ Placeholders: `<pm>`, `<org>`, `<api>`, `<feature>`, `<acao>`, `<screen>`, `<secao>`; exemplos usam `invoices` e `customers`. ADRs e `AGENTS.md` do repo vencem esta skill.
22
+
23
+ ## Assunto -> reference
24
+
25
+ | Abra quando for | Reference |
26
+ |---|---|
27
+ | criar ou alterar service, entities, domains, schemas, store da feature, mock, gateway ou query | [references/services.md](references/services.md) |
28
+ | criar ou alterar uma tela: grupo, `modules.tsx`, module, controller hook, service local, Safe Area, teclado, lista, permissão de dispositivo | [references/screens.md](references/screens.md) |
29
+ | mexer em boot, navigator, rota, params, deep link, login/logout/refresh, `secureStorage` ou env | [references/navigation-session.md](references/navigation-session.md) |
30
+ | mexer em componente comum, tema/tokens, marca, formulário, acessibilidade, performance ou observabilidade | [references/ui-mobile.md](references/ui-mobile.md) |
31
+ | testar, revisar PR, propor ADR ou fechar o critério de entrega | [references/review.md](references/review.md) |
32
+
33
+ ## Antes de editar
34
+
35
+ Levante no repo, nesta ordem:
36
+
37
+ ```txt
38
+ 1. package.json do app e da raiz: pm, scripts reais, lint/test vindos do monorepo
39
+ 2. versões: Expo, React Native, React Navigation ou expo-router, Zod, TanStack Query, store
40
+ 3. aliases: tsconfig.json, babel.config.js, metro.config.js
41
+ 4. App.tsx e index.ts: ordem de boot, providers raiz, overlays globais
42
+ 5. src/navigation: root/public/private, types.ts, linking.ts, hook de bootstrap
43
+ 6. screens.md (inventário de telas) e um grupo vizinho em src/screens/(private)/(<screen>)/
44
+ 7. uma feature vizinha em src/modules/<feature>: services, entities, domains
45
+ 8. common/services (ServiceResponse, api-error, dedupe, mappers comuns) e common/observability
46
+ 9. common/openapi/api.ts (fachada), types.ts e openapi-ts.config.ts (input e output)
47
+ 10. sessão: auth store, authedRequest, secureStorage, settings.storage.keys
48
+ 11. env: common/env/app.env.ts, .env.example, modo mock
49
+ 12. UI comum: common/components, common/theme, common/hooks, common/permissions
50
+ 13. testes existentes (*.unit.spec.ts) e onde o runner está configurado
51
+ 14. ADRs e AGENTS.md do app
52
+ ```
53
+
54
+ Pronto quando cada item tem um caminho no repo ou "não existe". O que não existe não é importado; script ausente vira lacuna declarada, não comando inventado.
55
+
56
+ ### Onde o repo varia
57
+
58
+ | Ponto | Padrão desta skill | Repo diferente: siga o repo |
59
+ |---|---|---|
60
+ | Navegação | React Navigation em `src/navigation` | expo-router: `app/_layout.tsx` faz o papel do navigator ([navigation-session.md](references/navigation-session.md)) |
61
+ | Fachada do client | `api.<org>.<api>.{client, settings}` em `common/openapi/api.ts` | outro nome: o critério é um ponto único para `baseUrl`, `cache` e headers globais |
62
+ | Request autenticado | `authedRequest` em `modules/auth/services/` | helper do repo; sem helper, o service lê o token do `secureStorage` |
63
+ | Modo mock | `environment.DATA_SOURCE_MODE === "MOCK"` | sem modo mock: o service não tem o ramo |
64
+ | Store | zustand em `*.store.hook.ts` | store do repo |
65
+ | Cache de servidor | TanStack Query (`AppQueryProvider`) | ausente: estado local + service; adotar pede ADR |
66
+ | Chaves de storage | `settings.storage.keys` | registro de chaves do repo |
67
+ | Testes | `*.unit.spec.ts` ao lado, runner do monorepo | outro runner ou nenhum: [review.md](references/review.md) |
68
+ | Marca dinâmica | `common/brand` | ausente: tokens do tema |
69
+ | `code` de erro | UPPER_SNAKE | formato do repo |
70
+
71
+ ## Mapa
72
+
73
+ ```txt
74
+ App.tsx index.ts app.config.ts app.json openapi-ts.config.ts screens.md (inventário de telas)
75
+ src/
76
+ navigation/
77
+ root.navigator.tsx splash até sessão e bootstrap resolverem; Public x Private; providers do fluxo privado
78
+ public.navigator.tsx private.navigator.tsx settings.navigator.tsx tab-routes.tsx
79
+ linking.ts types.ts use-app-bootstrap.hook.ts components/
80
+ screens/(public)/(<screen>)/ screens/(private)/(<screen>)/
81
+ <nome>.screen.tsx uma ou mais telas do grupo; fina: layout, safe area, Module.*
82
+ modules.tsx export const Module = { <Secao>: <Nome>Module }
83
+ modules/<secao>/<nome>.module.tsx composição visual da seção
84
+ modules/<secao>/*.component.tsx componente local da tela
85
+ hooks/use-<x>.hook.ts controller da tela: estado e chamadas a services, quando a tela orquestra vários
86
+ services/<x>.service.ts service LOCAL só quando a regra é exclusiva desta tela
87
+ modules/<feature>/
88
+ services/<acao>.<feature>.service.ts application layer: authedRequest + api.<org>.<api>.client.<operationId>; ServiceResponse
89
+ (algumas features usam <acao>-<objeto>.service.ts; ver "Nomes")
90
+ entities/<feature>.entities.ts namespace <Feature>.Model / .DTO derivado de Api.<Org>.<Api>.*
91
+ domains/<feature>.domain.ts (+ .unit.spec.ts) regra pura
92
+ schemas/<feature>.schemas.ts Zod para input de formulário e deep link
93
+ hooks/<x>.store.hook.ts hooks/use-<x>.hook.ts estado da feature compartilhado por telas
94
+ components/<x>.component.tsx componente de domínio usado por 2+ telas
95
+ mappers/<x>.mapper.ts tradução entre API e UI
96
+ mocks/ dados do modo mock
97
+ gateways/<x>.gateway.ts EXCEÇÃO: fonte fora do contrato OpenAPI (fetch nativo, nunca lança)
98
+ common/
99
+ components/<nome>/<nome>.component.tsx + index.ts
100
+ env/app.env.ts hooks/{feedbacks,loading,query,pagination,motion}/ observability/ permissions/ brand/ realtime/
101
+ openapi/api.ts client.ts types.ts <org>/<api>/setup/ (gerado)
102
+ providers/app.providers.tsx
103
+ services/service-response.ts api-error.ts in-flight-dedupe.ts
104
+ settings/{routes,storage,times} storage/secure-storage.ts theme/{default.theme.ts, tokens/}
105
+ utils/<tipo>/<nome>.<tipo>.util.ts (+ .unit.spec.ts)
106
+ ```
107
+
108
+ Colocação, do mais local ao mais compartilhado:
109
+
110
+ - Usado por uma tela: fica no grupo `screens/(…)/(<screen>)/` (`modules/`, `hooks/`, `services/`).
111
+ - Regra, chamada ou tipo usado por 2+ telas: `modules/<feature>/`. Feature é domínio, não tela.
112
+ - Componente de domínio usado por 2+ telas: `modules/<feature>/components/`.
113
+ - Visual sem domínio, reutilizável: `common/components/<nome>/`.
114
+ - Infra sem produto (client, env, storage, observabilidade, tema, permissões): `common/`.
115
+ - Promover é mover no segundo consumidor; até lá, local.
116
+
117
+ ## Camadas
118
+
119
+ | Camada | Arquivo | Responsabilidade |
120
+ |---|---|---|
121
+ | App | `App.tsx`, `index.ts` | ordem de boot, providers raiz, root navigator, overlays globais (status bar, loader, toast) |
122
+ | Navigation | `src/navigation/*` | rotas, ParamLists, linking, bootstrap de sessão, gate public/private |
123
+ | Screen | `<nome>.screen.tsx` | fina: Safe Area, layout, `Module.*`, controller hook; despacha variante por param |
124
+ | Mapa local | `modules.tsx` | `export const Module = { ... }` com os modules do grupo |
125
+ | Module | `modules/<secao>/<nome>.module.tsx` | interação da seção: estado de UI, chama service, trata `status`, feedback |
126
+ | Componente local | `modules/<secao>/<nome>.component.tsx`, `.modal.tsx` | visual da tela: props entram, eventos saem |
127
+ | Controller | `hooks/use-<x>.hook.ts` (no grupo) | estado e efeitos da tela (foco, polling, realtime), chama services, devolve dados e ações |
128
+ | Service local | `services/<x>.service.ts` (no grupo) | view model e chamadas exclusivas do grupo, com o contrato do service da feature |
129
+ | Service da feature | `services/<acao>.<feature>.service.ts` | um caso de uso por arquivo: regra, mock, request autenticado, `ServiceResponse` |
130
+ | Entities | `entities/<feature>.entities.ts` | `<Feature>.Model` / `.DTO` derivados de `Api.<Org>.<Api>.*` (fonte única) e tipos de UI que a API não tem |
131
+ | Domains | `domains/<feature>.domain.ts` | regra pura, limites, opções |
132
+ | Schemas | `schemas/<feature>.schemas.ts` | Zod do input de formulário, deep link e texto livre |
133
+ | Mappers | `mappers/<x>.mapper.ts` | tradução API ↔ UI |
134
+ | Hooks da feature | `hooks/<x>.store.hook.ts`, `hooks/use-<x>.hook.ts` | store de fluxo entre telas; hook reutilizado por 2+ telas |
135
+ | Components da feature | `components/<x>.component.tsx` | componente ou provider de domínio usado por 2+ telas |
136
+ | Mocks | `mocks/` | massa de dados do modo mock |
137
+ | Gateway (exceção) | `gateways/<x>.gateway.ts` | fonte fora do OpenAPI: `fetch` com timeout, resultado discriminado |
138
+ | Common | `src/common/*` | infra sem produto |
139
+
140
+ ## Nomes
141
+
142
+ | Arquivo | Export / formato |
143
+ |---|---|
144
+ | `<nome>.screen.tsx` | `<Nome>Screen`; props por `<Area>ScreenProps<"<Rota>">` |
145
+ | `<nome>.module.tsx` | `<Nome>Module`, registrado em `Module.<Secao>` |
146
+ | `<nome>.overview.module.tsx` | module principal da tela ou da aba de visão geral |
147
+ | `<nome>.component.tsx`, `<nome>.modal.tsx` | componente local; modal ou sheet local |
148
+ | `<nome>.step.tsx`, `<nome>.items.ts`, `<nome>.storage.ts` | etapa de fluxo multi-step; itens estáticos da tela (menu, opções); flag local não sensível |
149
+ | `use-<x>.hook.ts` | `use<X>` (controller: `use<X>Controller`) |
150
+ | `<x>.store.hook.ts` | `use<X>Store` |
151
+ | `<acao>.<feature>.service.ts` (feature no singular para item, plural para lista: `create.invoice`, `list.invoices`); algumas features usam `<acao>-<objeto>.service.ts` | `<nome em camelCase>Service`; na feature existente, siga a forma dela |
152
+ | `<feature>.entities.ts` | `namespace <Feature> { Model; DTO.<Acao>.{Input, ApiBody, Output} }` |
153
+ | `<feature>.schemas.ts` | `<feature>Schemas = { <acao>: z.object(...) }` + `namespace <Feature>Schema` |
154
+ | `<feature>.domain.ts` | constantes `UPPER_SNAKE` e funções puras |
155
+ | `<x>.mapper.ts` | `map<X>` (função pura) |
156
+ | `<x>.gateway.ts` | `fetch<X>Gateway`, tipo `<X>GatewayResult` |
157
+ | `<area>.navigator.tsx` | `<Area>Navigator` |
158
+ | `<nome>.<tipo>.util.ts` | utilitário em `common/utils/<tipo>/`, com `index.ts` |
159
+ | `*.unit.spec.ts` | teste ao lado do arquivo testado |
160
+ | evento de observabilidade | `<feature>.<acao>` (`invoices.create`) |
161
+ | `code` de erro | `<FEATURE>_<ACAO>_FAILED` ou o motivo de negócio (`INVOICE_REQUIRES_LINES`) |
162
+
163
+ ## Caminho de uma ação
164
+
165
+ ```txt
166
+ module ou controller (onPress)
167
+ -> <acao>.<feature>.service.ts
168
+ withServiceObservability("<feature>.<acao>", async () => {
169
+ regra de negócio -> serviceFailure("<CODE>", mensagem) (inline ou domains/)
170
+ modo mock? -> serviceSuccess(mock)
171
+ authedRequest((accessToken) => api.<org>.<api>.client.<operationId>({
172
+ baseUrl, cache, headers: Authorization, path, query, body }))
173
+ res.error -> serviceFailure("<FEATURE>_<ACAO>_FAILED", apiErrorMessage(res.error, fallback))
174
+ (ou o mapper de falha comum, quando a API devolve uma família de erro conhecida)
175
+ sucesso -> serviceSuccess(res.data.data) (envelope desembrulhado)
176
+ })
177
+ <- ServiceResponse
178
+ module ou controller
179
+ status "success" -> atualiza estado, navega, feedback de sucesso
180
+ status "error" -> useFeedbackHook.show({ message, mode: "error" }); fieldErrors no formulário
181
+ espera -> ação que trava a tela: useLoadingHook.show() e hide() no finally
182
+ ação local: estado submitting do próprio module
183
+ ```
184
+
185
+ Leitura de tela: `screen -> controller (efeito de foco) -> list service (dedupe em voo) -> estado -> Module.<Lista>`.
186
+
187
+ ## Regras não negociáveis
188
+
189
+ 1. **Screen fina.** Screen e module falam com services; `common/openapi` é importado só por services (da feature ou locais) e por entities (`import type`).
190
+ 2. **Request pelo service.** O service chama `api.<org>.<api>.client.<operationId>` com `baseUrl` e `cache` de `api.<org>.<api>.settings`; rota autenticada vai dentro de `authedRequest`, que entrega o token e renova a sessão no 401; rota pública (login, cadastro, recuperação) chama o client sem token.
191
+ 3. **Gateway é exceção.** `gateways/` serve só fonte fora do contrato OpenAPI: `fetch` com timeout, devolve `{ data } | { error }` e nunca lança. Operação que existe no contrato vai pelo client gerado.
192
+ 4. **`ServiceResponse` sempre.** Todo service roda dentro de `withServiceObservability("<feature>.<acao>", ...)` e devolve `serviceSuccess` ou `serviceFailure`; a UI decide por `status`.
193
+ 5. **Erro com `code` estável e mensagem útil.** `code` UPPER_SNAKE do app; mensagem da API (`apiErrorMessage(error, fallback)`) quando ela explica o motivo, texto do service quando não; status HTTP e corpo do erro ficam fora da tela (diagnóstico vai para o log). App com i18n segue o web: mensagem traduzida pelo `code`; a da API fica no log.
194
+ 6. **Input tipado e validado.** O service recebe o DTO de `entities`; formulário, deep link e texto livre passam pelo Zod de `schemas/` antes de chegar ao service; regra que barra a chamada é `serviceFailure` antes do request.
195
+ 7. **Mock dentro do service.** O ramo do modo mock vive só no service, depois das regras e antes do request; module e screen chamam o service igual nos dois modos.
196
+ 8. **Sessão no `secureStorage`.** Token e usuário da sessão ficam só no `secureStorage` (wrapper de `expo-secure-store`) com chaves de `settings.storage.keys`; o header `Authorization` nasce no callback do `authedRequest`.
197
+ 9. **Gate no root.** O root navigator segura o splash até o bootstrap resolver a sessão e escolhe `Public` ou `Private`; providers do fluxo privado envolvem só o `PrivateNavigator`.
198
+ 10. **Params tipados e leves.** Toda rota tem ParamList em `navigation/types.ts`; params levam ids e dicas leves de exibição (título, thumbnail, estado vindo da lista); objeto inteiro e dado sensível ficam fora, e o resto vem do service.
199
+ 11. **Env fail-closed.** `EXPO_PUBLIC_*` vai inteiro para o bundle: só valor não secreto. Schema Zod em `common/env/app.env.ts`, `assertAppEnv()` no `AppProviders`, leitura no uso (getter ou função), https fora de local/dev.
200
+ 12. **Log com redaction.** Logs passam pelo `logger` de `common/observability`; `console.*` e devtools ficam sob `__DEV__` (exceção: o diagnóstico de env inválido, que precisa aparecer em release).
201
+ 13. **Tela completa.** Safe Area, teclado e estados loading/error/empty em toda tela; lista longa em `FlatList`/`FlashList` (pelo wrapper de `common/components`, quando existe).
202
+ 14. **Gerado não se edita.** `common/openapi/<org>/<api>/setup/` sai do `openapi-ts.config.ts`; operação faltando, contrato errado ou `operationId` ruim se corrige na API (`arch-api`) e se regenera com `openapi:generate`.
203
+ 15. **Boundaries.** `common` não importa `screens` nem `modules`; `domains` e `entities` não importam React/RN.
204
+ 16. **Gate de entrega.** Cada script da tabela "Comandos" roda no seu gatilho; o inventário `screens.md` do app reflete as telas do diff; mudança nativa declara iOS e Android testados ou "não testado".
205
+
206
+ ## Protocolo: nova tela
207
+
208
+ ```txt
209
+ 1. Leia o inventário screens.md do app e um grupo vizinho de complexidade parecida.
210
+ Pronto: você cita o path do grupo que serve de modelo.
211
+ 2. Escolha o grupo: rotas que dividem modules, controller ou service local (lista e detalhe, etapas de um
212
+ fluxo, variantes) ficam no mesmo grupo; assunto sem nada em comum ganha (<screen>)/ novo.
213
+ Variante da mesma rota por param: "Variantes da mesma rota" em references/screens.md.
214
+ Pronto: o grupo escolhido e o motivo cabem em uma frase.
215
+ 3. Registre a rota: ParamList em types.ts, Stack.Screen ou tab no navigator, linking.ts se há deep link.
216
+ Pronto: navigation.navigate("<Rota>", params) compila sem cast.
217
+ 4. Crie <nome>.screen.tsx fina, modules.tsx e os modules por seção.
218
+ Pronto: a screen só importa Module, o controller (se houver), tipos de navegação e componentes de layout.
219
+ 5. Controller hook e service local: só pelo critério de [references/screens.md](references/screens.md).
220
+ 6. Service: reutilize ou crie em modules/<feature> (protocolo abaixo).
221
+ 7. Feche a regra 13 com a mecânica de references/screens.md.
222
+ Pronto: cada fluxo assíncrono da tela tem carregando, erro, vazio e conteúdo.
223
+ 8. Atualize o inventário screens.md do app. Pronto: a linha da tela aponta grupo, rota e status.
224
+ 9. Rode typecheck. Pronto: verde, ou o erro relatado com arquivo e linha.
225
+ ```
226
+
227
+ ## Protocolo: novo service
228
+
229
+ ```txt
230
+ 1. Confirme a operação no client gerado (sdk.gen.ts / operationId); faltando ou errada: regra 14.
231
+ Pronto: api.<org>.<api>.client.<operationId> existe e tipa.
232
+ 2. DTO em entities: <Feature>.DTO.<Acao>.{Input, ApiBody, Output} derivados de Api.<Org>.<Api>.*.
233
+ Pronto: o service não declara tipo de request ou response à mão.
234
+ 3. Regra pura reutilizável ou que merece teste: domains/ (+ .unit.spec.ts).
235
+ 4. Escreva <acao>.<feature>.service.ts pelo template de [references/services.md](references/services.md):
236
+ observabilidade -> regra -> mock -> authedRequest -> erro -> sucesso.
237
+ 5. Leitura que várias telas ou efeitos disparam ao mesmo tempo: dedupe em voo (references/services.md).
238
+ 6. Integre no module ou controller pelo "Caminho de uma ação". Pronto: os dois status têm tratamento na UI.
239
+ 7. Spec da regra pura quando há runner. Pronto: typecheck verde e spec verde (ou lacuna declarada).
240
+ ```
241
+
242
+ ## Comandos
243
+
244
+ Descubra os nomes reais no `package.json`; em monorepo, `<pm> --filter <app> <script>`.
245
+
246
+ | Script | Gatilho |
247
+ |---|---|
248
+ | `typecheck` | toda mudança TypeScript (gate mínimo) |
249
+ | `openapi:generate` | operação nova ou alterada na API |
250
+ | `openapi:check` | contrato ou client gerado mudou |
251
+ | `deps:check`, `doctor` | dependência, plugin Expo, `app.json`/`app.config`, asset nativo |
252
+ | `start`, `prebuild`, builds de plataforma | validar em device ou simulador |
253
+ | lint e test | os do monorepo, quando existirem |
254
+
255
+ ## Anti-padrões
256
+
257
+ Sintomas que aparecem no diff, cada um com a regra que resolve.
258
+
259
+ - `import { api }` ou `fetch(` num module, screen ou controller → regras 1 e 3.
260
+ - `if (environment.DATA_SOURCE_MODE` fora de um service → regra 7 (o app de referência tem uma exceção num module de upload; não copie).
261
+ - Service devolvendo `res.data` com o envelope quando o consumidor espera o item → "Caminho de uma ação".
262
+ - `throw` num service para erro esperado da API → regra 4.
263
+ - Objeto inteiro do recurso passado em `navigation.navigate` → regra 10.
264
+ - `environment.<X>` ou `appEnv.<X>` lido no escopo do módulo → regra 11.
265
+ - `ScrollView` com `.map` numa lista que cresce → regra 13.
266
+ - Arquivo em `common/openapi/<org>/<api>/setup/` alterado sem `openapi:generate` → regra 14.
267
+ - Componente, hook ou service duplicado entre grupos de tela → colocação.
268
+
269
+ ## Critério de entrega
270
+
271
+ ```txt
272
+ [ ] inventário feito; o padrão do vizinho foi seguido (path citado)
273
+ [ ] regras 1-16 conferidas no diff, cada uma ok ou "não se aplica"
274
+ [ ] protocolo da mudança (tela, service) completo até o último passo
275
+ [ ] scripts rodados no gatilho, com resultado, ou lacuna declarada
276
+ [ ] divergências do repo em relação a esta skill registradas, com migração incremental
277
+ [ ] resumo com paths concretos e o que não foi testado em device
278
+ ```
@@ -0,0 +1,3 @@
1
+ interface:
2
+ display_name: "Arch App"
3
+ short_description: "Implementa, revisa e refatora apps React Native Expo: boundaries..."
@@ -0,0 +1,257 @@
1
+ # Navegação, sessão e env
2
+
3
+ Abra ao mexer em boot, navigator, rota, params, deep link, login/logout/refresh, `secureStorage` ou env. As regras (gate no root, params leves, sessão no `secureStorage`, env fail-closed) estão no SKILL.md; aqui fica a mecânica.
4
+
5
+ ## Ordem de boot
6
+
7
+ ```txt
8
+ index.ts
9
+ 1. import do handler global de erro (primeira linha: erro JS não tratado vira alerta nativo, não tela branca)
10
+ 2. polyfills que outros módulos exigem no import (ex.: crypto.getRandomValues)
11
+ 3. devtools só em __DEV__ (require dentro do if)
12
+ 4. registerRootComponent(App)
13
+ App.tsx
14
+ GestureHandlerRootView > KeyboardProvider > ErrorBoundary > AppProviders >
15
+ RootNavigator + StatusBar + overlays globais (loader, toast)
16
+ AppProviders (common/providers/app.providers.tsx)
17
+ assertAppEnv() -> SafeAreaProvider -> AppQueryProvider -> children
18
+ ```
19
+
20
+ `assertAppEnv()` roda dentro do `ErrorBoundary`: env inválido mostra a mensagem do schema na tela em vez de derrubar o app no import.
21
+
22
+ ## Root navigator
23
+
24
+ ```tsx
25
+ // src/navigation/root.navigator.tsx
26
+ const Stack = createNativeStackNavigator<RootStackParamList>();
27
+
28
+ export function RootNavigator() {
29
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
30
+ const isLoading = useAuthStore((state) => state.isLoading);
31
+ const { minSplashElapsed, onboardingSeen } = useAppBootstrap();
32
+
33
+ // Splash até sessão, onboarding e tempo mínimo resolverem: o Public não mostra um segundo splash.
34
+ if (isLoading || !minSplashElapsed || onboardingSeen === null) return <SplashScreen />;
35
+
36
+ return (
37
+ <NavigationContainer linking={linking}>
38
+ <Stack.Navigator screenOptions={{ headerShown: false }}>
39
+ {!isAuthenticated ? (
40
+ <Stack.Screen name="Public" navigationKey="public">
41
+ {() => <PublicNavigator onboardingSeen={onboardingSeen} />}
42
+ </Stack.Screen>
43
+ ) : (
44
+ <Stack.Screen name="Private" navigationKey="private">
45
+ {() => (
46
+ <RealtimeProvider>
47
+ <DomainProvider>{/* provider de domínio de uma feature */}
48
+ <PrivateNavigator />
49
+ <NotificationBanner />
50
+ </DomainProvider>
51
+ </RealtimeProvider>
52
+ )}
53
+ </Stack.Screen>
54
+ )}
55
+ </Stack.Navigator>
56
+ </NavigationContainer>
57
+ );
58
+ }
59
+ ```
60
+
61
+ - A troca `Public` ↔ `Private` acontece pelo `isAuthenticated` da auth store; `navigationKey` descarta o histórico da outra pilha.
62
+ - Providers do fluxo privado (regra 9): realtime, provider de domínio de uma feature (`modules/<feature>/components/`), e overlays que dependem deles (banner de notificação) como irmãos do `PrivateNavigator`.
63
+ - Telas e services chamam `setUser`/`clearUser` da store; nenhuma tela navega para `Public`/`Private` à mão.
64
+
65
+ ## Navigators
66
+
67
+ ```txt
68
+ public.navigator.tsx pilha anônima (onboarding, login, cadastro, recuperação)
69
+ private.navigator.tsx pilha privada: rota de bottom tabs (`MainTabs`) + rotas empilhadas sobre as tabs
70
+ settings.navigator.tsx pilha aninhada de configurações, registrada como uma rota do private
71
+ tab-routes.tsx TAB_ROUTES (name, component, label, onPress?), ícones e a rota central
72
+ components/ peças só da navegação (tab bar customizada, header de pilha)
73
+ ```
74
+
75
+ - Rota nova em pilha: `<Stack.Screen name="<Rota>" component={<Nome>Screen} options={{ animation }} />` no navigator da área; a screen vem do grupo em `src/screens/(…)/(<screen>)/`.
76
+ - Opções visuais (`animation`, `presentation: "fullScreenModal"`, `contentStyle`) ficam no `Stack.Screen`; cor por token do tema.
77
+ - Tab nova: entrada em `TAB_ROUTES` e chave em `PrivateTabParamList`.
78
+ - Overlay de todas as telas privadas que não depende de provider do root (assistente flutuante) é irmão do `Stack.Navigator` dentro do `PrivateNavigator`.
79
+
80
+ ## `types.ts`
81
+
82
+ ```ts
83
+ export type PublicStackParamList = { Welcome: undefined; SignIn: undefined };
84
+ export type PrivateTabParamList = { Dashboard: undefined; CustomersTab: undefined };
85
+ export type PrivateStackParamList = {
86
+ MainTabs: NavigatorScreenParams<PrivateTabParamList>;
87
+ Invoices: { customer?: string } | undefined;
88
+ InvoiceDetail: {
89
+ invoice: string;
90
+ kind?: "draft" | "issued";
91
+ /** dica de exibição enquanto o detalhe carrega */
92
+ title?: string;
93
+ };
94
+ CreateInvoice: { customer?: string } | undefined;
95
+ };
96
+ export type RootStackParamList = { Public: undefined; Private: undefined };
97
+
98
+ export type PrivateScreenProps<T extends keyof PrivateStackParamList> = NativeStackScreenProps<PrivateStackParamList, T>;
99
+ ```
100
+
101
+ - Uma ParamList por pilha ou tab; um alias `<Area>ScreenProps<T>` por ParamList.
102
+ - Param opcional: `{ ... } | undefined`, para a rota abrir sem params (menus que navegam por `keyof` exigem).
103
+ - A dica de exibição pinta a tela antes do service responder; quando o service responde, vale o dado dele.
104
+ - Param que decide permissão ou estado sensível é conferido pela API na chamada do service.
105
+
106
+ ## `linking.ts`
107
+
108
+ ```ts
109
+ function resolvePrefixes(): string[] {
110
+ try {
111
+ return [Linking.createURL("/")];
112
+ } catch {
113
+ return []; // build sem scheme: deep link desligado, app sobe
114
+ }
115
+ }
116
+
117
+ export const linking: LinkingOptions<RootStackParamList> = {
118
+ prefixes: resolvePrefixes(),
119
+ config: {
120
+ screens: {
121
+ Public: { screens: { SignIn: "sign-in" } },
122
+ Private: {
123
+ screens: {
124
+ MainTabs: { screens: { Dashboard: "dashboard" } },
125
+ InvoiceDetail: "invoices/:invoice",
126
+ },
127
+ },
128
+ },
129
+ },
130
+ };
131
+ ```
132
+
133
+ - `createURL` roda no import e lança em build sem scheme; o `try/catch` mantém o app de pé.
134
+ - A árvore de `config.screens` espelha a árvore de navigators; rota com deep link entra aqui no mesmo PR.
135
+ - Deep link para rota privada com usuário anônimo cai no `Public`; param de deep link chega como string (regra 6).
136
+
137
+ ## Bootstrap de sessão
138
+
139
+ `src/navigation/use-app-bootstrap.hook.ts` roda uma vez no root:
140
+
141
+ ```txt
142
+ 1. tempo mínimo de splash (timer)
143
+ 2. sincronizações com fallback embutido (ex.: manifesto de marca): fire-and-forget, o splash não espera
144
+ 3. flags locais (onboarding visto) lidas do storage
145
+ 4. sessão:
146
+ a. identidade de instalação: instalação nova ou restore em outro aparelho descarta a sessão herdada do keychain
147
+ b. access token no secureStorage e não expirado -> restaura o usuário salvo
148
+ c. senão -> refresh; autenticado -> restaura o usuário; falhou -> clearUser()
149
+ d. qualquer exceção -> clearUser(); finally -> setLoading(false)
150
+ ```
151
+
152
+ Usuário salvo é JSON no `secureStorage`; parse inválido vira `clearUser()`.
153
+
154
+ ## Auth store e `secureStorage`
155
+
156
+ - Auth store (`modules/auth/hooks/auth.store.hook.ts`): `user`, `isAuthenticated`, `isLoading`, `setUser`, `clearUser`, `setLoading`. Guarda o usuário para a UI; tokens ficam fora dela.
157
+ - `common/storage/secure-storage.ts` envolve `expo-secure-store` com cache em memória:
158
+
159
+ ```ts
160
+ const cache = new Map<string, string | null>();
161
+
162
+ export const secureStorage = {
163
+ get: async (key: string) => {
164
+ const cached = cache.get(key);
165
+ if (cached !== undefined) return cached; // `null` lido também é memorizado
166
+ const value = await SecureStore.getItemAsync(key);
167
+ cache.set(key, value);
168
+ return value;
169
+ },
170
+ set: async (key: string, value: string) => {
171
+ try { await SecureStore.setItemAsync(key, value); cache.set(key, value); }
172
+ catch (error) { cache.delete(key); throw error; } // write nativo falhou: o cache não afirma nada
173
+ },
174
+ delete: async (key: string) => {
175
+ try { await SecureStore.deleteItemAsync(key); cache.set(key, null); }
176
+ catch (error) { cache.delete(key); throw error; }
177
+ },
178
+ clearCache: () => cache.clear(),
179
+ };
180
+ ```
181
+
182
+ - O cache evita idas ao keychain a cada request; ele só fica coerente porque todo write da aplicação passa pelo wrapper. Módulo que precisa de chave própria fora do wrapper usa chaves próprias e documenta.
183
+ - Chaves em `common/settings/storage/keys.storage.ts`, expostas como `settings.storage.keys.<chave>` (`accessToken`, `refreshToken`, `user`, ...).
184
+ - Flag não sensível (onboarding visto, preferência local) pode usar storage comum, num `<nome>.storage.ts` ao lado de quem lê.
185
+
186
+ ## Refresh, 401 e logout
187
+
188
+ - O retry no 401 é do `authedRequest` ([services.md](services.md)); ele e o bootstrap usam o mesmo service de refresh (`refresh-session.auth.service.ts`).
189
+ - Renovação única: requests concorrentes com 401 esperam a mesma promise de refresh (requisito e lacuna em [services.md](services.md)).
190
+ - Refresh que falha limpa tokens e usuário; a auth store volta a anônimo e o root troca para `Public`.
191
+ - Logout resiliente: tenta revogar na API sem bloquear; apaga `accessToken`, `refreshToken` e `user` do `secureStorage`; `clearUser()`.
192
+
193
+ ```ts
194
+ export async function logoutAuthService(): Promise<ServiceResponse<null>> {
195
+ return withServiceObservability("auth.logout", async () => {
196
+ const refreshToken = await secureStorage.get(settings.storage.keys.refreshToken);
197
+ if (refreshToken) {
198
+ // Revogação na API pelo client gerado; API fora do ar não bloqueia o logout local.
199
+ await api.<org>.<api>.client.<logoutOperationId>({ baseUrl: api.<org>.<api>.settings.url, body: { refreshToken } })
200
+ .catch(() => undefined);
201
+ }
202
+ await Promise.all([
203
+ secureStorage.delete(settings.storage.keys.accessToken),
204
+ secureStorage.delete(settings.storage.keys.refreshToken),
205
+ secureStorage.delete(settings.storage.keys.user),
206
+ ]);
207
+ useAuthStore.getState().clearUser();
208
+ return serviceSuccess(null);
209
+ });
210
+ }
211
+ ```
212
+
213
+ ## Env
214
+
215
+ ```ts
216
+ // src/common/env/app.env.ts
217
+ const schema = z
218
+ .object({
219
+ API_URL: z.string().url(),
220
+ APP_ENV: z.enum(["local", "development", "staging", "production"]).default("production"),
221
+ OBSERVABILITY_ENABLED: z.enum(["true", "false"]).default("true").transform((v) => v === "true"),
222
+ DATA_SOURCE_MODE: z.enum(["MOCK", "REAL"]).default("REAL"),
223
+ })
224
+ .refine((env) => env.APP_ENV === "local" || env.APP_ENV === "development" || env.API_URL.startsWith("https://"), {
225
+ path: ["API_URL"],
226
+ message: "API_URL deve usar https:// quando APP_ENV é staging ou production.",
227
+ });
228
+
229
+ const raw = {
230
+ API_URL: process.env.EXPO_PUBLIC_API_URL, // acesso estático: é o que o bundler inlina
231
+ APP_ENV: process.env.EXPO_PUBLIC_APP_ENV,
232
+ OBSERVABILITY_ENABLED: process.env.EXPO_PUBLIC_OBSERVABILITY_ENABLED,
233
+ DATA_SOURCE_MODE: process.env.EXPO_PUBLIC_DATA_SOURCE_MODE,
234
+ };
235
+ const result = schema.safeParse(raw);
236
+
237
+ export const error: Error | null = result.success ? null : new Error(/* issues + valores recebidos */);
238
+
239
+ // Válido: objeto plano. Inválido: Proxy que lança com a mensagem acima em qualquer leitura.
240
+ export const environment = result.success
241
+ ? result.data
242
+ : (new Proxy({}, { get: (_t, key) => { throw new Error(`[environment] leitura de "${String(key)}" com env inválido.\n\n${error?.message}`); } }) as z.infer<typeof schema>);
243
+
244
+ export function assertAppEnv(): void {
245
+ if (error) throw error;
246
+ }
247
+ ```
248
+
249
+ - A mensagem de erro lista as issues e os valores crus recebidos no bundle, para o diagnóstico apontar a variável ausente (o default de `APP_ENV` esconde um `undefined`).
250
+ - Além do getter da regra 11: código que roda em todo service e lê uma flag (`withServiceObservability`) envolve a leitura em `try/catch` e assume o valor seguro.
251
+ - `common/env/index.ts` reexporta o mesmo objeto validado (na referência, como `appEnv`); siga o nome que o vizinho importa.
252
+ - Variável nova: schema, `raw`, `.env.example` e configuração de build no mesmo PR.
253
+ - Segredo que o app precisa vem da API ou da sessão em tempo de execução.
254
+
255
+ ## expo-router
256
+
257
+ Repo com expo-router: siga o repo. `app/_layout.tsx` faz o papel do root navigator (providers, splash até o bootstrap resolver, redirecionamento por sessão), o `_layout.tsx` dos grupos `(public)`/`(private)` faz o papel dos navigators de área, e cada arquivo de rota em `app/` delega para a screen em `src/screens/(…)/(<screen>)/`. Params chegam por `useLocalSearchParams` como string e passam pelo schema antes de uso sensível. `screens`, `modules`, `common`, services e sessão seguem esta skill sem mudança.