orquestra-mcp 1.10.0 → 1.16.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 +7 -1
- package/package.json +3 -3
- package/src/api.mjs +13 -0
- package/src/platformPlaybooks.mjs +248 -0
- package/src/server.mjs +518 -31
- package/src/skillInstall.mjs +90 -0
- package/src/skillsCatalog.mjs +151 -0
- package/src/stages.mjs +3 -2
package/README.md
CHANGED
|
@@ -115,6 +115,12 @@ Lectura de contexto:
|
|
|
115
115
|
| `get_parity_summary` | `projectId?` | Desfases de paridad multiplataforma (tasks y modules por separado) |
|
|
116
116
|
| `get_workspace_summary` | — | Progreso agregado de todos los proyectos del workspace |
|
|
117
117
|
| `get_stage_playbook` | `stageId` | Plantilla de módulo+tareas para una etapa (`pre`\|`build`\|`post`) — sin red |
|
|
118
|
+
| `get_platform_playbook` | `platform` | Guía de referencia + checklist de publicación en tienda (`ios`\|`android`) — sin red |
|
|
119
|
+
| `get_skills_catalog` | — | Catálogo de Claude Skills de terceros recomendadas — sin red |
|
|
120
|
+
| `preview_skill` | `key?` / `sourceRepo?`+`sourcePath?` | Trae el SKILL.md real de una skill de terceros, sin instalar nada |
|
|
121
|
+
| `install_skill` | `key?` / `sourceRepo?`+`sourcePath?`, `scope`, `confirmed` | Descarga e instala una skill en `.claude/skills/` — solo después de `preview_skill` + confirmación del usuario; además registra la instalación en Orquestra (best-effort) |
|
|
122
|
+
| `list_installed_skills` | `projectId?` | Registros reales de skills instaladas en el proyecto (colección `skills`, no el catálogo) |
|
|
123
|
+
| `create_skill_install` / `update_skill_install` | ver server.mjs | Self-report manual de una skill instalada a mano, fuera del flujo de `install_skill` |
|
|
118
124
|
| `get_setup_guide` | — | Orden recomendado (flujo→categorías→infra→módulos→tareas→ideas→notas→changelog→docs) para configurar/sincronizar sin duplicar — sin red |
|
|
119
125
|
| `list_modules` / `list_statuses` / `list_categories` / `list_services` / `list_environments` / `list_distributions` | `projectId?` | Lista completa de cada entidad — revísalos antes de `create_*` para no duplicar (patrón upsert) |
|
|
120
126
|
|
|
@@ -189,7 +195,7 @@ create/update sigue siendo criterio del agente y del humano.
|
|
|
189
195
|
38 tools: 9 de lectura (contexto/tasks/search + dashboard/workspace/infra/
|
|
190
196
|
paridad/setup guide/stage playbook) + 6 `list_*` + 19 de creación/edición
|
|
191
197
|
(tasks/ideas/changelog/notes/docs + módulos/flujo/categorías/servicios/
|
|
192
|
-
entornos/distribuciones/proyecto) + 2 de
|
|
198
|
+
entornos/distribuciones/proyecto) + 2 de Agentes (create_agent_run/get_agent_run) +
|
|
193
199
|
2 compuestas (init_project, apply_stage_playbook). No hay `delete_*`, ni
|
|
194
200
|
`update_idea`/`update_note`/`update_doc`/`update_changelog_entry`
|
|
195
201
|
todavía. Publicado en npm como `orquestra-mcp` (unscoped) -- `npx
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orquestra-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Servidor MCP de Orquestra --
|
|
3
|
+
"version": "1.16.0",
|
|
4
|
+
"description": "Servidor MCP de Orquestra -- 89 tools para leer y escribir tareas, ideas, changelog, infraestructura, contenido, QA y más desde un agente de IA",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"orquestra-mcp": "
|
|
7
|
+
"orquestra-mcp": "bin/orquestra-mcp.mjs"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
package/src/api.mjs
CHANGED
|
@@ -102,3 +102,16 @@ export async function apiDelete(config, path, { projectId, id }) {
|
|
|
102
102
|
})
|
|
103
103
|
return parseResponse(res)
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
// PUT crudo a una presigned URL de S3 (createAttachmentUploadUrl en
|
|
107
|
+
// orquestra-infra) -- a diferencia de apiGet/apiPost/etc, este request NO
|
|
108
|
+
// va contra config.apiBase ni lleva el Bearer del MCP: la URL firmada YA
|
|
109
|
+
// es la autorización completa.
|
|
110
|
+
export async function putToPresignedUrl(uploadUrl, buffer, contentType) {
|
|
111
|
+
const res = await fetch(uploadUrl, {
|
|
112
|
+
method: 'PUT',
|
|
113
|
+
headers: { 'Content-Type': contentType },
|
|
114
|
+
body: buffer,
|
|
115
|
+
})
|
|
116
|
+
if (!res.ok) throw new Error(`Falló la subida a S3: HTTP ${res.status}`)
|
|
117
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// Puerto exacto de orquestra-web/src/lib/platformPlaybooks.js (PLATFORM_PLAYBOOKS) --
|
|
2
|
+
// constantes estaticas, sin acceso a red ni a la base de datos. get_platform_playbook
|
|
3
|
+
// lo lee directo desde aca (mismo criterio que stages.mjs/foundations.mjs).
|
|
4
|
+
export const PLATFORM_PLAYBOOKS = {
|
|
5
|
+
|
|
6
|
+
ios: {
|
|
7
|
+
label: 'iOS — App Store',
|
|
8
|
+
color: '#007AFF',
|
|
9
|
+
moduleName: 'Publicación en App Store',
|
|
10
|
+
moduleDescription: 'Checklist verificable para enviar la app a revisión en App Store Connect, sin sorpresas de último momento.',
|
|
11
|
+
moduleTags: ['playbook', 'publicacion'],
|
|
12
|
+
|
|
13
|
+
referenceGuide: [
|
|
14
|
+
{
|
|
15
|
+
key: 'name',
|
|
16
|
+
title: 'Name',
|
|
17
|
+
body: 'El nombre de marca tal cual. Apple no permite keyword-stuffing en el nombre desde 2017.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
key: 'subtitle',
|
|
21
|
+
title: 'Subtitle (máx. 30 caracteres)',
|
|
22
|
+
body: 'Una frase corta que diga a quién sirve y qué resuelve, no un slogan genérico. Cuenta caracteres literal.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: 'category',
|
|
26
|
+
title: 'Category',
|
|
27
|
+
body: 'Primary + Secondary. Piensa en la categoría real de uso. "Developer Tools" está separada de "Business" -- si la app es una herramienta del ciclo de vida de software, Developer Tools calza mejor que Business.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: 'content_rights',
|
|
31
|
+
title: 'Content Rights',
|
|
32
|
+
body: 'Pregunta si la app contiene/muestra/accede a contenido de terceros. Apunta a apps que REDISTRIBUYEN contenido con derechos de terceros (streaming, stock media) -- no a "cualquier dato que pase por la app". Si solo se muestran datos propios del usuario/equipo (o conectados desde su propia cuenta, ej. su propio repo de GitHub), la respuesta es "No".',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: 'age_features',
|
|
36
|
+
title: 'Age Ratings — Paso 1 (Features)',
|
|
37
|
+
body: 'Formulario sobre controles/capacidades técnicas (Parental Controls, Age Assurance, Unrestricted Web Access, User-Generated Content, Social Media, Messaging and Chat, Advertising). Ojo con "User-Generated Content": la definición de Apple es sobre "broad distribution" (distribución amplia/pública) -- si el contenido queda dentro de un workspace/equipo privado entre miembros invitados, NO cuenta como UGC para esta pregunta. Para una app B2B típica todo va en "No".',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'age_content',
|
|
41
|
+
title: 'Age Ratings — Pasos 2-6 (descriptores de contenido)',
|
|
42
|
+
body: 'Mature Themes, Medical/Wellness, Sexuality/Nudity, Violence, Chance-Based Activities -- para una app sin ese tipo de contenido, todo en "None"/"No". El resultado esperado es clasificación 4+.',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: 'age_override',
|
|
46
|
+
title: 'Age Ratings — Paso 7 (Age Categories and Override)',
|
|
47
|
+
body: 'Deja "Not Applicable" salvo que la app esté realmente dirigida a niños (Kids Category) o tenga una razón real para pedir una clasificación más alta.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'pricing',
|
|
51
|
+
title: 'App Pricing',
|
|
52
|
+
body: 'Depende del modelo de negocio real del producto -- no asumas que es gratis, esto lo decide cada proyecto.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: 'availability',
|
|
56
|
+
title: 'App Availability',
|
|
57
|
+
body: '"All Countries or Regions" es el default razonable salvo que haya una restricción regulatoria real específica de la app.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: 'privacy_choices_url',
|
|
61
|
+
title: 'User Privacy Choices URL (opcional)',
|
|
62
|
+
body: 'Junto a la Privacy Policy URL, App Store Connect deja un campo opcional para una URL de "elecciones de privacidad" (ej. opt-out de venta/uso de datos con fines publicitarios, al estilo CCPA). Solo aplica si la app realmente vende/comparte datos con terceros para publicidad -- para una app sin SDKs de publicidad ni venta de datos (como Orquestra), se deja en blanco.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: 'data_collection',
|
|
66
|
+
title: 'Data Collection (App Privacy)',
|
|
67
|
+
body: 'Apple pide declarar, por cada tipo de dato que la app recolecta, para qué se usa (App Functionality / Analytics / Advertising / etc.), si está vinculado a la identidad del usuario, y si se usa para "tracking" (definición estricta de Apple: compartir con OTRAS empresas para publicidad/medición cross-app, no cualquier analítica interna). Para una app típica de Orquestra (Firebase Auth + Firestore + Analytics + Crashlytics, sin ads): declara Name/Email/User ID/User Content como vinculados a identidad y con propósito "App Functionality"; Device ID y Product Interaction como Analytics (vinculados a identidad SOLO si el código llama a Analytics.setUserID/Crashlytics.setUserID con el uid real -- revisar caso por caso); nada se usa para tracking si no hay SDKs de publicidad/ATT.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: 'delete_account',
|
|
71
|
+
title: 'Eliminar cuenta desde la app (Guideline 5.1.1(v))',
|
|
72
|
+
body: 'Obligatorio desde 2022: si la app permite crear cuenta, tiene que permitir ELIMINARLA desde DENTRO de la app -- "escríbenos por correo" no cumple el requisito, aunque sea rápido y real. Verifica que exista un flujo de borrado self-service (botón "Eliminar cuenta" en Perfil/Ajustes, con confirmación) ANTES de enviar a revisión -- si no existe, Apple puede rechazar la app durante el review, incluso si el resto de la metadata está perfecta. Orquestra ya tiene este flujo (Zona de riesgo en Perfil, backend server-side que borra cuenta+datos), úsalo de referencia de implementación si tu app lo necesita desde cero.',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
|
|
76
|
+
tasks: [
|
|
77
|
+
{
|
|
78
|
+
title: 'Verificar/crear política de privacidad pública (URL)',
|
|
79
|
+
description: 'App Store Connect pide una URL pública de política de privacidad antes de poder enviar a revisión. Si el proyecto no tiene una, hay que publicarla (puede vivir en la landing).',
|
|
80
|
+
type: 'business', priority: 'high', tags: ['publicacion', 'ios'], offsetDays: 2, checkKey: 'privacy',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
title: 'Verificar/crear página o contacto de soporte',
|
|
84
|
+
description: 'Apple pide una URL de soporte visible para el usuario (puede ser un mailto, un formulario o una página de FAQ).',
|
|
85
|
+
type: 'business', priority: 'high', tags: ['publicacion', 'ios'], offsetDays: 2, checkKey: 'support',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
title: 'Confirmar pricing y disponibilidad por país',
|
|
89
|
+
description: 'Definir el modelo de precio real (gratis, de pago, suscripción) y el alcance de países -- "All Countries or Regions" es el default razonable salvo restricción regulatoria específica.',
|
|
90
|
+
type: 'business', priority: 'low', tags: ['publicacion', 'ios'], offsetDays: 3,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title: 'Preparar cuenta demo para el reviewer, si el login lo requiere',
|
|
94
|
+
description: 'Si la app pide login, "App Review Information" necesita un usuario y contraseña de prueba (o notas de acceso) para que el reviewer entre sin fricción.',
|
|
95
|
+
type: 'business', priority: 'medium', tags: ['publicacion', 'ios'], offsetDays: 5,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
title: 'Preparar capturas de pantalla en los tamaños exactos que pide App Store Connect',
|
|
99
|
+
description: 'Las dimensiones válidas cambian con cada generación de iPhone/iPad -- si App Store Connect rechaza una captura al subirla, el mensaje de error indica el tamaño exacto que espera para ese dispositivo; ajusta según ese mensaje real, no de memoria.',
|
|
100
|
+
type: 'business', priority: 'medium', tags: ['publicacion', 'ios'], offsetDays: 7,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
title: 'Completar el cuestionario de privacidad de la tienda (Data Collection)',
|
|
104
|
+
description: 'Por cada tipo de dato que la app recolecta (nombre, email, user ID, contenido del usuario, device ID, product interaction), declarar el propósito (App Functionality / Analytics / etc.), si está vinculado a la identidad y si se usa para tracking (definición estricta de Apple: compartir con otras empresas para publicidad/medición cross-app).',
|
|
105
|
+
type: 'business', priority: 'high', tags: ['publicacion', 'ios'], offsetDays: 9,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
title: 'Completar clasificación de edad / Age Rating',
|
|
109
|
+
description: 'Recorrer los 7 pasos del cuestionario de Age Ratings en App Store Connect: Features, los 5 descriptores de contenido y Age Categories/Override. Para una app B2B sin ese contenido, el resultado esperado es 4+.',
|
|
110
|
+
type: 'business', priority: 'medium', tags: ['publicacion', 'ios'], offsetDays: 10,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
title: 'Confirmar que la app permite eliminar la cuenta desde adentro',
|
|
114
|
+
description: 'Guideline 5.1.1(v), obligatorio -- "escríbenos por correo" NO cumple el requisito. Tiene que haber un botón real de "Eliminar cuenta" (con confirmación) en Perfil/Ajustes que borre la cuenta y los datos sin salir de la app. Si no existe, constrúyelo ANTES de mandar a revisión, no después de un rechazo.',
|
|
115
|
+
type: 'security', priority: 'high', tags: ['publicacion', 'ios', 'compliance'], offsetDays: 1,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
android: {
|
|
121
|
+
label: 'Android — Google Play',
|
|
122
|
+
color: '#3DDC84',
|
|
123
|
+
moduleName: 'Publicación en Google Play',
|
|
124
|
+
moduleDescription: 'Checklist verificable para publicar la app en Google Play Console, sin sorpresas de último momento.',
|
|
125
|
+
moduleTags: ['playbook', 'publicacion'],
|
|
126
|
+
|
|
127
|
+
referenceGuide: [
|
|
128
|
+
{
|
|
129
|
+
key: 'app_details',
|
|
130
|
+
title: 'App details: name (30) / package name / default language / app o game / free o paid',
|
|
131
|
+
body: 'Nombre de marca, máx. 30 caracteres. Package name (applicationId) no se puede cambiar después de publicar -- verifícalo dos veces. Default language: si el producto es en español, elige "Spanish (Latin America)" si está disponible, o "Spanish (Mexico)" antes que "Spanish (Spain)" (vocabulario distinto). "App" (no Game). Free o Paid depende del modelo de negocio real -- una vez publicada como Free no se puede cambiar a Paid.',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
key: 'contact_details',
|
|
135
|
+
title: 'Store listing contact details',
|
|
136
|
+
body: 'Email de contacto (obligatorio, se muestra a los usuarios), teléfono en formato internacional completo con código de país (opcional), sitio web. "External marketing" (dejar que Google promocione la app fuera de Play Store) no tiene costo ni implica que tú corras ads -- déjalo activado salvo razón real para lo contrario.',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
key: 'category',
|
|
140
|
+
title: 'Categoría',
|
|
141
|
+
body: 'A diferencia de Apple (primary + secondary), Play Console solo pide UNA categoría principal. Piensa en el uso real: para una herramienta del ciclo de vida de software, "Productividad" suele calzar mejor que "Negocios" según cómo se use de verdad.',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
key: 'text_assets',
|
|
145
|
+
title: 'Common text assets: Short description (80) / Full description (4000)',
|
|
146
|
+
body: 'Short description: 80 caracteres, aparece en la lista de resultados de búsqueda -- escríbela pensando en beneficio + audiencia, no un slogan vacío. Full description: hasta 4000 caracteres, es la ficha completa -- beneficios primero, después una lista real de funciones (sin inventar features que la app no tiene), sección aparte para IA opcional si aplica, y a quién está dirigida al final. Cuenta caracteres literal, Play Console rechaza el guardado si te pasas.',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
key: 'visual_assets',
|
|
150
|
+
title: 'Common visual assets: ícono, feature graphic, capturas',
|
|
151
|
+
body: 'Ícono: PNG/JPEG hasta 1MB, 512x512px exacto. Feature graphic: PNG/JPEG hasta 15MB, 1024x500px exacto -- se usa cuando Google destaca la app, dejar margen real (~10% del ancho) en los bordes para que no se corte contenido importante en distintas pantallas. Capturas de teléfono: PNG/JPEG hasta 8MB cada una, mínimo 2 y máximo 8, cada lado entre 320-3840px -- para ser elegible a promoción, al menos 4 capturas con mínimo 1080px por lado. Video (opcional): URL de YouTube, público o no listado, sin ads ni restricción de edad.',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
key: 'privacy_policy',
|
|
155
|
+
title: 'Privacy policy URL',
|
|
156
|
+
body: 'Obligatoria para publicar -- Play Console valida que la URL responda 200 al guardarla, no solo que tenga formato de URL. Ojo con apps de una sola página (React Router y similares): si el hosting no tiene configurado el rewrite de SPA (servir index.html para cualquier ruta), una URL como /privacidad puede dar 404 real aunque el deploy haya salido bien -- probar la URL en el navegador ANTES de pegarla acá, no confiar en que "ya se desplegó" alcanza.',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
key: 'ads_declaration',
|
|
160
|
+
title: 'Ads declaration',
|
|
161
|
+
body: '"Does your app contain ads?" -- "No" si no hay SDKs de publicidad integrados (incluye SDKs de terceros que puedan traer publicidad de forma indirecta, revisa las dependencias reales del proyecto, no solo lo que agregaste tú a propósito).',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: 'declarations',
|
|
165
|
+
title: 'Financial features / Government apps / Health apps (declaraciones "ninguna de las anteriores")',
|
|
166
|
+
body: 'Tres formularios separados que preguntan por funciones específicas (préstamos, pagos, cripto / apps gubernamentales / salud y fitness). Para una app de gestión de proyectos típica, la respuesta a los tres es la opción "mi app no ofrece [eso]" -- no marques nada de la lista de features específicas antes de esa opción.',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
key: 'advertising_id',
|
|
170
|
+
title: 'Advertising ID',
|
|
171
|
+
body: '"Does your app use advertising ID?" -- revisa el manifest/build.gradle antes de contestar: si ningún SDK (ni siquiera Firebase Analytics en su config default) pide el permiso `com.google.android.gms.permission.AD_ID` ni hay dependencias tipo `play-services-ads`, la respuesta real es "No". No lo asumas, grepealo.',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
key: 'content_rating',
|
|
175
|
+
title: 'Content rating questionnaire (IARC)',
|
|
176
|
+
body: 'Cuestionario largo, con sub-preguntas que se despliegan según tus respuestas. Puntos donde el criterio NO es obvio: "User Content Sharing" (¿la app permite nativamente intercambiar texto/voz/imágenes/audio entre usuarios?) es MÁS AMPLIO que el "User-Generated Content" de Apple -- Apple exime contenido privado dentro de un equipo, Google NO tiene esa excepción, así que comentarios/notas compartidas entre miembros de un workspace SÍ cuentan como "Yes" acá aunque sean privados. Si contestas "Yes" ahí, "¿las interacciones pueden limitarse a usuarios invitados?" responde "Yes" si el acceso es por invitación (esto ayuda a mantener la clasificación baja). El resto (violencia, sexual, lenguaje, drogas, apuestas) va en "No"/"None" para una app B2B típica -- resultado esperado: la clasificación más baja (3+/PEGI 3/Everyone).',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
key: 'target_audience',
|
|
180
|
+
title: 'Target audience and content',
|
|
181
|
+
body: 'Selecciona SOLO "18 and over" si la app es una herramienta de trabajo para adultos -- marcar rangos de edad menores obliga a cumplir políticas adicionales de seguridad infantil que no aplican. Activa también "Restrict users that Google has determined to be minors" (opcional pero gratis y consistente con lo anterior).',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
key: 'data_safety',
|
|
185
|
+
title: 'Data safety: por cada tipo de dato -- collected/shared, ephemeral, required/optional, purpose',
|
|
186
|
+
body: 'Para cada categoría marcada (Name, Email, User IDs, Other user-generated content, App interactions, Crash logs, Diagnostics, Device or other IDs son las típicas de una app Firebase sin ads): (1) "Collected" sí, "Shared" NO si el dato solo va a tu propio backend/Firebase (proveedor de infraestructura, no es "compartir con terceros"). (2) "¿Procesado de forma efímera?" NO si se persiste (Firestore, Crashlytics, Analytics todo persiste). (3) "¿Requerido u opcional?" -- required si no hay forma de usar la app sin ese dato (no hay toggle real para desactivarlo). (4) Purpose: Name/Email/User Content = "App functionality"; User ID también "Account management" y a veces "Analytics" si el mismo ID se manda a Analytics/Crashlytics (revisa si el código llama `Analytics.setUserId`/`Crashlytics.setUserId` con el uid real); Crash logs/Diagnostics/Device ID/App interactions = "Analytics" (el propio ejemplo de Google dice "diagnosticar bugs o crashes" bajo Analytics, a diferencia de Apple que lo pone en "App Functionality" -- mismo dato, categoría distinta según la tienda).',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
key: 'app_access',
|
|
190
|
+
title: 'App access / Sign-in details',
|
|
191
|
+
body: 'Si la app requiere login, "Add sign in details" pide un set de credenciales de prueba REALES (nombre descriptivo del set, usuario/email, contraseña) que el reviewer va a usar de verdad para entrar -- tienen que funcionar y, si es posible, mostrar contenido de ejemplo real (no una cuenta vacía cayendo en un onboarding). Marca el checkbox de "esta cuenta da acceso completo a todas las funciones" si no hay niveles pagos/premium.',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
key: 'delete_account',
|
|
195
|
+
title: 'Eliminar cuenta desde la app (política de borrado de cuenta de Google Play)',
|
|
196
|
+
body: 'Equivalente a la Guideline 5.1.1(v) de Apple: si la app permite crear cuenta, tiene que ofrecer un camino para eliminarla -- lo ideal es un flujo real DENTRO de la app (mismo estándar que Apple), y además Play Console pide una "Delete account URL" pública (puede ser la misma página de soporte, con instrucciones claras de qué se borra/conserva y por cuánto tiempo). Verifica que esa URL responda 200 antes de pegarla -- ver el punto de "Privacy policy URL" arriba, mismo gotcha real de SPA rewrite.',
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
|
|
200
|
+
tasks: [
|
|
201
|
+
{
|
|
202
|
+
title: 'Verificar/crear política de privacidad pública (URL)',
|
|
203
|
+
description: 'Play Console (Contenido de la app) pide una URL pública de política de privacidad antes de poder publicar. Si el proyecto no tiene una, hay que publicarla (puede vivir en la landing).',
|
|
204
|
+
type: 'business', priority: 'high', tags: ['publicacion', 'android'], offsetDays: 2, checkKey: 'privacy',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
title: 'Verificar/crear página o contacto de soporte',
|
|
208
|
+
description: 'Play Console pide un email de contacto de soporte visible en la ficha de Play Store.',
|
|
209
|
+
type: 'business', priority: 'high', tags: ['publicacion', 'android'], offsetDays: 2, checkKey: 'support',
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
title: 'Probar en el navegador que la URL de privacidad/soporte responde 200 (no solo que "ya se desplegó")',
|
|
213
|
+
description: 'Gotcha real: Play Console valida que la URL responda, no solo el formato. Un deploy exitoso no garantiza que rutas como /privacidad carguen -- si el hosting es una SPA (React Router y similares) sin la regla de rewrite configurada (servir index.html para cualquier ruta), da 404 real aunque el build haya salido bien. Probar la URL directo en el navegador, en una pestaña nueva, ANTES de pegarla en el formulario.',
|
|
214
|
+
type: 'tech', priority: 'high', tags: ['publicacion', 'android'], offsetDays: 2,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
title: 'Confirmar pricing y disponibilidad por país',
|
|
218
|
+
description: 'Definir si la app es gratis o de pago y en qué países se distribuye -- "Disponible en todos los países" es el default razonable salvo restricción regulatoria específica.',
|
|
219
|
+
type: 'business', priority: 'low', tags: ['publicacion', 'android'], offsetDays: 3,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
title: 'Preparar cuenta demo para el reviewer, si el login lo requiere',
|
|
223
|
+
description: 'Si la app pide login, la sección "Acceso a la app" de Play Console necesita credenciales de prueba (usuario/contraseña) o instrucciones de acceso para que el reviewer entre sin fricción -- lo ideal es que la cuenta caiga en contenido de ejemplo real, no en un onboarding vacío.',
|
|
224
|
+
type: 'business', priority: 'medium', tags: ['publicacion', 'android'], offsetDays: 5,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
title: 'Confirmar que la app permite eliminar la cuenta desde adentro',
|
|
228
|
+
description: 'Política de borrado de cuenta de Google Play (equivalente a la Guideline 5.1.1(v) de Apple) -- lo ideal es un flujo real dentro de la app, no solo la "Delete account URL" de la ficha. Si no existe, constrúyelo antes de publicar.',
|
|
229
|
+
type: 'security', priority: 'high', tags: ['publicacion', 'android', 'compliance'], offsetDays: 1,
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
title: 'Preparar capturas de pantalla en los tamaños exactos que pide Play Console',
|
|
233
|
+
description: 'Mínimo 2 capturas de teléfono, más el gráfico destacado (1024x500) y el ícono (512x512) -- Play Console valida las dimensiones al subir, ajusta según lo que rechace.',
|
|
234
|
+
type: 'business', priority: 'medium', tags: ['publicacion', 'android'], offsetDays: 7,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
title: 'Completar el formulario de seguridad de los datos (Data safety)',
|
|
238
|
+
description: 'Declarar, por cada tipo de dato que la app recolecta o comparte, para qué se usa y si viaja cifrado -- mismo criterio que la declaración de privacidad de iOS.',
|
|
239
|
+
type: 'business', priority: 'high', tags: ['publicacion', 'android'], offsetDays: 9,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
title: 'Completar clasificación de contenido (cuestionario IARC)',
|
|
243
|
+
description: 'Cuestionario de clasificación de contenido de Play Console. Para una app B2B sin violencia/sexual/apuestas, el resultado esperado es la clasificación más baja.',
|
|
244
|
+
type: 'business', priority: 'medium', tags: ['publicacion', 'android'], offsetDays: 10,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
}
|