| Estudiante | Edad | País | Escuela | Fecha | Estado | Acciones |
|---|---|---|---|---|---|---|
| Sin estudiantes | ||||||
| Nombre | Rol | Plan | Institución | Solicitado | Estado | Acciones | |
|---|---|---|---|---|---|---|---|
| Cargando... | |||||||
Parte A (secciones 1–7): cómo funciona el informe, cómo leerlo con un estudiante, cómo mantener el catálogo de carreras y cómo verificar el algoritmo antes de un piloto. Parte B (secciones 8–14): arquitectura, mapa del código, modelo de datos, flujos, convenciones y seguridad, para que un programador pueda seguir haciendo cambios. Sección 15 (v3.2, 30/09/2026): módulo Orientación Laboral. Caso de referencia: piloto de Bolivia (liceo de Potosí, 56 estudiantes).
Parte A — Funcionamiento y algoritmo
1. Qué mide el test y qué devuelve
El test es un tamizaje multidimensional. Mide cinco cosas: intereses (RIASEC/Holland, seis tipos: Realista, Investigador, Artístico, Social, Emprendedor, Convencional, con percentiles normados “pc”), fortalezas y confianza (ocho inteligencias múltiples de Gardner, autoreportadas), razonamiento (ejercicios de comprensión lectora, razonamiento, vocabulario y matemática, más matrices tipo Raven), personalidad (Big Five) y valores (qué busca el estudiante en un trabajo). Se completan además el torneo de carreras (elegir entre dos) y la “isla ideal”.
El informe devuelve un ranking de carreras (cuatro recomendadas y dos para investigar), un perfil narrativo, las gráficas de cada dimensión ordenadas de mayor a menor, el plan de 30 días, preguntas de reflexión, una sección para padres y una nota para el orientador. Todo el cálculo se hace en el navegador, de forma determinística: dos estudiantes con las mismas respuestas obtienen el mismo resultado. La función de IA está en el código pero no se usa hoy.
2. Cómo se calcula el ranking (versión v3, vigente desde el 08/09/2026)
Cada carrera del catálogo tiene dos vectores: un perfil RIASEC (seis valores de 1 a 5) y un perfil de inteligencias (ocho valores de 1 a 5). Para cada estudiante, el algoritmo compara su perfil con el de cada carrera y calcula un puntaje. Los pesos vigentes son:
| Componente | Peso | Qué hace |
|---|---|---|
| Intereses (RIASEC) | 55% | Normalizados dentro del propio estudiante (su tipo más alto vale 1, el más bajo 0), ponderados por el perfil RIASEC de la carrera. Es el predictor principal. |
| Fortalezas (inteligencias) | 20% | Igual lógica, con el perfil de inteligencias de la carrera. Si el estudiante marcó todo igual, este bloque se anula. |
| Personalidad (Big Five) | 10% | Responsabilidad, Apertura y Estabilidad emocional como moderador global. |
| Valores | 10% | Si los valores elegidos coinciden con los dos tipos RIASEC dominantes de la carrera, suma. |
| Razonamiento | ajuste fino ±4 pts | Ya no forma parte de la base. Suma o resta hasta 4 puntos al match de cada carrera según el desempeño cognitivo. |
Sobre esa base se aplican tres ajustes: un bono por el torneo de carreras (hasta +10 por carreras elegidas repetidamente), un bono por la isla ideal (+4 si el tipo dominante de la carrera coincide) y la regla de umbral matemático: solo las carreras de muy alta exigencia matemática (valor 5 en Lógico-matemática: ingenierías, ciencias exactas, informática, finanzas) se multiplican por 0,85 si el estudiante tuvo menos de 40% en matemática y por 0,72 si tuvo menos de 25%, con una advertencia visible en el informe. Hoy son 28 de las 105 carreras bolivianas; antes de la v3 eran 63.
El puntaje crudo se reescala a una escala de 35 a 96 y se comprime hacia el centro cuando los seis intereses quedaron muy parejos (menos de 15 puntos entre el más alto y el más bajo). Las etiquetas son: Máxima (82 o más), Alta (68 a 81), Media (54 a 67) y Explorable (menos de 54).
Dos reglas nuevas de la v3 sobre la lista final: en las cuatro carreras recomendadas hay como máximo dos de la misma área (evita que un perfil convencional vea cuatro variantes de Contaduría), y si la diferencia entre el interés más alto y el más bajo es menor a 12 puntos, el informe se marca como perfil plano: el resumen deja de afirmar una vocación, presenta las carreras como puntos de partida para explorar, y la nota al orientador lo dice explícitamente con el rango en puntos.
Qué esperar en un grupo: en una simulación de 60 perfiles aleatorios realistas, la primera carrera salió Alta en el 65% de los casos, Media en el 30% y Máxima en el 5%; en el conjunto de las cuatro recomendadas hubo también algunas Explorable. Si un grupo entero sale Alta o entero sale Media, algo está mal en los datos de entrada.
3. Catálogo de carreras por país
Si el estudiante tiene país Bolivia en su ficha, el ranking usa el catálogo boliviano (CATALOGO_BO): 105 carreras en tres niveles (74 licenciaturas universitarias, 20 técnico superior, 11 de las Escuelas Superiores de Formación de Maestros). Fuentes: oferta de UMSA, UAGRM y UPSA, Guía de Institutos Técnicos y Tecnológicos y convocatoria ESFM del Ministerio de Educación. Para cualquier otro país se usa el catálogo general. El campo país se carga en “Nuevo estudiante” y también lo completa el estudiante al registrarse; si queda vacío o “no especificado”, cae al catálogo general, así que conviene revisarlo antes de enviar los links.
Cada carrera boliviana se define en una fila [nombre, código RIASEC de 3 letras, área, nivel, descripción, entorno laboral]. El código RIASEC genera el vector de intereses (5/4/3 para las tres letras, 1 para el resto) y el área genera el vector de inteligencias base (doce áreas: salud, agro, ciencias, ing, info, empresa, admin, social, docencia, arte, turismo, oficio). Desde la v3 existe además la tabla AJUSTES_BO, que corrige por carrera cualquier valor de esos dos vectores. Ejemplo: 'Agropecuaria': {g:{LO:3,CN:5}} baja la exigencia matemática a 3 y sube lo corporal a 5; 'Ingeniería Electrónica': {r:{I:5,R:4}, g:{ES:3,CN:3}} la vuelve más investigadora que realista. Gracias a esa tabla, las 105 carreras tienen hoy perfiles distintos (antes solo había 56 perfiles para 105 carreras, con un grupo de 10 ingenierías idénticas).
Para agregar una carrera: sumar una fila a CARRERAS_BO_ROWS() con el código RIASEC y el área correctos y, si comparte código y área con otra ya existente, agregar una entrada en AJUSTES_BO que la diferencie. Para cambiar cómo se comporta una carrera (por ejemplo, si un orientador nota que Enfermería sale demasiado poco), tocar solo su entrada en AJUSTES_BO; no hace falta tocar la lógica. Las claves válidas son r con letras R, I, A, S, E, C y g con LI (lingüística), LO (lógico-matemática), ES (espacial), MU (musical), CN (corporal-kinestésica), IN (interpersonal), IP (intrapersonal), NA (naturalista), siempre con valores de 1 a 5. Recordar que LO=5 activa la regla matemática: asignarlo solo cuando la carrera realmente exige matemática avanzada.
4. Cómo leer el informe con el estudiante (para el orientador)
Primero mirar la nota para el orientador al final del informe: dice el tipo dominante con su porcentaje y el rango en puntos entre el tipo más alto y el más bajo. Un rango de 25 o más es un perfil nítido; entre 12 y 25 es moderado; menos de 12 es perfil plano y el informe ya lo marca. En ese caso no se conversa sobre “cuál es tu carrera” sino sobre qué actividades de exploración hacer antes (entrevistar a un profesional, visitar una universidad o instituto, probar el torneo de carreras de nuevo en unas semanas).
Segundo, las cuatro carreras y sus etiquetas. Alta o Máxima significa que el perfil completo es consistente con esa carrera; Media significa que hay afinidad parcial y vale la pena mirar qué componente falta; Explorable significa que es la mejor opción disponible pero con poca evidencia, cosa que pasa sobre todo en perfiles planos. Desde la v3 las cuatro carreras vienen de al menos dos áreas distintas, así que la conversación puede contrastar caminos y no solo variantes de uno.
Tercero, la advertencia matemática cuando aparece. No es una descalificación: dice que esa carrera exige matemática avanzada y que hoy el estudiante tuvo un resultado bajo en esa prueba. Se convierte en plan de refuerzo, no en descarte.
Cuarto, el nivel de cada carrera (Licenciatura, Técnico Superior o Normal · ESFM). El ranking mezcla los tres niveles con el mismo peso; corresponde al orientador conversar sobre qué nivel es realista para cada familia (duración, costo, ciudad donde se dicta). Para Potosí, la Universidad Autónoma Tomás Frías y los institutos técnicos del departamento cubren parte del catálogo; el resto implica mudarse a Sucre, La Paz, Cochabamba o Santa Cruz.
Preguntas que abren la conversación: ¿con cuál te sentís identificado? ¿cuál te sorprende? ¿cuál habrías puesto vos que no está? Esta última es la más valiosa: si el estudiante nombra una carrera que el informe dejó abajo, se puede buscar por qué (casi siempre es un interés bajo en Realista o un componente de inteligencia que el estudiante subestimó al responder).
5. Cómo verificar el algoritmo antes de un piloto
Todo el motor es accesible desde la consola del navegador con la sesión de orientador abierta en la app. La función generarResultadoLocal(e, riasec, gardner, cognitivo, valores, extra, islaTop, islaSeleccion, learning, bigfive, pares) devuelve el informe completo. Un perfil de prueba se arma así: e = {nombre, edad, pais:'Bolivia'}; riasec = {R,I,A,S,E,C} en porcentajes; gardner con las ocho inteligencias por nombre en porcentajes; cognitivo = {'Comprensión lectora','Razonamiento','Vocabulario','Matemática', total}; valores como lista de textos de la pantalla de valores; bigfive con los cinco rasgos por nombre; pares como objeto carrera → cantidad de victorias en el torneo. El resultado trae carreras (nombre, match, afinidad, advertencia, nivel), carreras_investigar, perfil_resumen, nota_metodologica y recomendacion_orientador.
La batería mínima antes de cada cambio son seis perfiles: realista-agro con matemática baja, investigador STEM, artístico visual-musical, perfil plano, emprendedor-social y convencional. Para cada uno se controla que las cuatro carreras sean coherentes con el tipo dominante, que las etiquetas no sean todas iguales entre perfiles, que el perfil plano se marque como tal, y que en el realista con matemática baja aparezcan carreras agropecuarias y técnicas (es el caso que fallaba antes de la v3). Conviene además controlar que CATALOGO_BO() siga teniendo 105 perfiles distintos: si dos carreras comparten vector, empatan y el orden lo decide la posición en el archivo.
Resultado de la batería con la v3 en producción: realista-agro → Construcción Civil, Topografía, Ingeniería Forestal, Agropecuaria (Media); STEM → Matemática, Ingeniería Informática, Sistemas Informáticos, Física (Alta); artístico → Lingüística e Idiomas, Comunicación Social, Comunicación Estratégica, Literatura (Alta); plano → Enfermería, Nutrición, Ciencias de la Educación, Ciencias Ambientales (Media, con texto de exploración); emprendedor-social → Derecho, Turismo, Turismo técnico, Relaciones Internacionales (Alta); convencional → Contaduría Pública, Banca y Seguros, Ingeniería Comercial, Administración (Alta).
6. Cómo se publica un cambio
El código vive en el repositorio privado paulavelazco25-hue/gps-vocacional, archivo index.html; Netlify redespliega automáticamente cada commit a main en uno o dos minutos. Se puede editar desde el editor web de GitHub (con la sesión de Pau) o desde la copia local en Downloads/gps-vocacional-fix con commit y push. Los informes ya generados no cambian con un deploy: quedan guardados en Firestore tal como se calcularon; solo los tests que se completen después usan la lógica nueva. Si se quiere regenerar el de un estudiante, existe la opción “regenerar” en el panel.
Historial reciente: 30/09/2026 (v3.2) — módulo Orientación Laboral (sección 15), función netlify/functions/ia.js, colección laboral. 08/09/2026 — barras ordenadas de mayor a menor en informe y PDF; catálogo Bolivia con 105 vectores únicos vía AJUSTES_BO; umbral matemático solo para LO=5; razonamiento como ajuste fino ±4; detección de perfil plano; máximo dos carreras por área en el top 4; textos de “¿Cómo calculamos?” y nota metodológica actualizados. 15/07/2026 — ponderación v2, mensajes de aliento, A/B del orden de secciones.
7. Limitaciones conocidas y próximos pasos
El interés Realista pesa mucho en las carreras de diseño y arquitectura por cómo están codificadas (A-R-I), así que un artístico con Realista muy bajo recibe más carreras de comunicación y letras que de diseño; corregirlo implica revisar los códigos RIASEC del área arte. El nivel (universidad / técnico / ESFM) no se pregunta al estudiante; una preferencia previa ordenaría mejor. El catálogo es nacional y no distingue qué se dicta en Potosí. Si una sección cognitiva queda sin responder, el informe muestra 0% en lugar de “sin datos”. Y la función de IA (llamarIA) está definida pero sin uso; conectarla permitiría una narrativa más rica, con el costo y la variabilidad que eso implica.
Parte B — Manual para desarrolladores
Esta parte describe la app tal como está construida hoy, para que cualquier programador pueda ubicarse en el código y hacer cambios sin romper lo que funciona. Todo lo que sigue se relevó del código en producción (septiembre 2026).
8. Arquitectura en una página
GPS Vocacional es una single-page application en un único archivo index.html (~6.100 líneas, ~400 KB) sin framework, sin build y sin bundler: HTML, CSS y JavaScript plano en el mismo archivo. Se sirve como sitio estático desde Netlify (gpsvocacionalapp.netlify.app), con deploy automático en cada commit a main del repo privado paulavelazco25-hue/gps-vocacional.
El backend es Firebase: Authentication (email + contraseña) y Cloud Firestore como base de datos, accedidos directamente desde el navegador con el SDK compat 10.12.2 (firebase-app-compat, firebase-auth-compat, firebase-firestore-compat). No hay servidor propio; la única lógica fuera del navegador son dos Cloudflare Workers (worker-gps-v2.js y worker_registro.js en el repo) que hablan con Firestore por REST, con Mailchimp y con Resend/Stripe. El PDF se genera en el navegador con jsPDF 2.5.1 (cdnjs). No hay otras dependencias.
Flujo de datos, de punta a punta: el orientador crea un estudiante (documento en estudiantes) y obtiene un link con ?test=<sid>&token=<token>; el estudiante abre el link sin cuenta, la app cierra cualquier sesión y carga el test; al terminar, el navegador calcula el informe con generarResultadoLocal y guarda resultado en el mismo documento; el orientador lo ve en su panel y puede exportarlo a PDF.
9. Mapa del archivo index.html
El archivo tiene cuatro bloques <script> y el HTML de las vistas al final. Los números de línea son aproximados (versión del 08/09/2026) y sirven para orientarse; buscar por nombre de función es más seguro.
| Zona | Líneas aprox. | Qué contiene |
|---|---|---|
| Script 1 (cabecera) | 5–26 | window.onerror de seguridad (muestra el login si el JS falla), metas PWA. |
| CDN | 27–30 | jsPDF y Firebase compat. |
| Script 2 (config) | 31–61 | firebaseConfig, auth, db, y wrappers con nombres del SDK modular (getDoc, setDoc, updateDoc, deleteDoc, getDocs, query, collection, doc, onAuthStateChanged, etc.) sobre la API compat. Usar siempre estos wrappers, no db.collection() directo. |
| Script 3 (app) | 62–~5.400 | Toda la lógica: planes, auth, panel, test, algoritmo, informe, PDF, admin, institución, comparar, recursos. |
| Script 4 (arranque) | ~5.400–5.520 | Lectura de ?test=&token=, onAuthStateChanged, repairAdminDoc, gestión de planes admin. |
| HTML | ~5.520–6.121 | #view-auth (login/registro), #view-test (test del estudiante), #view-app (panel con sidebar y las páginas #page-*). |
Dentro del Script 3, en orden:
| Bloque | Funciones clave | Notas |
|---|---|---|
| Planes y permisos | PLANES (l.67), getPlan, canUse, esInstitucion, esPlanInstitucional, showUpgradeModal | Planes: free, starter, profesional, premium, inst_small, inst_medium, inst_large. Cada plan define límites (maxEstudiantes, maxPDFs, carrerasVisibles, verInformeCompleto, exportPDF, notaOrientador, marcaBlanca, dashboardAvanzado). |
| Utilidades | escapeHTML, toast, showModal, closeModal, showLoading, formatDate, initials | escapeHTML existe pero casi no se usa (ver Seguridad). |
| Auth | doLogin, doRegister (orientador), doRegisterFree (estudiante autónomo), doLogout, doForgotPassword | Al registrarse se crea el doc usuarios/{uid} con rol, estado, plan. |
| Sesión y panel | initApp (l.553), showPage (l.686), updateDashboard, renderEstudiantes, crearEstudiante, buildTestLink, resetearTest, deleteEstudiante | session es el objeto global con uid + campos del doc de usuario; showPage(id) activa #page-<id> y marca el nav (navMap). El bloque #admin-nav solo se muestra si session.rol==='admin'. |
| Test | TEST_STEPS (l.~990), TEST_STEPS_CORTO, pasos(), abVariante(), initTestView, renderTestStep, testNext/testPrev, guardarProgreso/cargarProgreso | Los pasos son objetos {title, instruccion, items, type}; tipos: mixed (Likert), cognitivo, raven, pares (torneo), más valores e isla ideal. Progreso en localStorage['gps_progress_'+sid] (expira a los 7 días). A/B: la variante B mueve el torneo al inicio. |
| Cierre del test | testSubmit → doSubmitFinal (l.~2199) | Calcula calcRIASEC, calcGardner, calcLearningStyle, calcBigFive, calcCognitivo, calcRaven, calcularPercentiles, calcCalidad; llama a generarResultadoLocal; guarda {tokenUsado:true, resultado, abVariante}; avisa al worker /tag-complete; muestra showTestSuccess. llamarIA y buildPrompt existen pero no se llaman. |
| Catálogo y algoritmo | CARRERAS_BO_ROWS, G_AREA_BO, TAGS_AREA_BO, AJUSTES_BO, CATALOGO_BO, generarResultadoLocal (con CARRERAS_GEN adentro), score, afinidadLabel | Ver Parte A, secciones 2 y 3. |
| Informe | openInforme (l.~3059), abrirEdicion/guardarEdicion, downloadPDF (l.~3397) | openInforme(sid) arma el HTML del informe del orientador; la vista del estudiante es renderMiResultado/showTestSuccess. Cualquier cambio visual del informe suele tocar tres lugares: vista estudiante, vista orientador y PDF. |
| Admin e institución | aprobarOrientador, cambiarPlanOrientador, loadAdminConfig/saveAdminConfig, renderInstDashboard, exportarCohorteCSV, renderInstOrientadores | config/plataforma guarda nombre, web y mensaje de bienvenida (marca blanca). |
| Comparar y recursos | initComparar, mostrarComparacion, MATERIALES_DEF, renderMateriales, descargarMaterial | Materiales del método (pilares/sesiones) embebidos en el código. |
| Manual | #page-admin-manual (HTML) | Este documento. showPage redirige al panel si el rol no es admin. |
10. Modelo de datos (Firestore)
usuarios/{uid} — una cuenta por orientador, institución, estudiante autónomo o admin. Campos: nombre, titulo, inst, contacto, email, pais, rol (orientador | estudiante | institucion | admin), estado (aprobado | pendiente | rechazado), plan (clave de PLANES), fechaReg, pdfCount, onboardingVisto; para estudiantes autónomos además autoSid y autoToken (apuntan a su propio doc en estudiantes); para orientadores de una institución, institucionId.
estudiantes/{sid} — un test. sid es 's' + uuid sin guiones. Campos: nombre, edad, escuela, anio, pais, obs, emailAlumno, orientadorId, orientadorNombre, fecha, token (64 hex, se compara con el de la URL), tokenUsado, resultado (null hasta completar), abVariante, testIniciadoAt, banderas esAutoTest / esDemo, y notaOrientador cuando el orientador edita. resultado contiene: carreras[] (nombre, match, afinidad, descripcion, entorno_laboral, tags, nivel, advertencia), carreras_investigar[], riasec, gardner, bigfive, cognitivo, percentiles, valores, learning, perfil_resumen, fortalezas, areas_desarrollo, entornos_laborales, plan_30_dias, preguntas_reflexion, para_padres, proximos_pasos, recomendacion_orientador, nota_metodologica, calidad.
config/plataforma — nombre, web, welcomeMsg (marca blanca, lo edita el admin).
baremos/global — histograma acumulado de RIASEC y Big Five (N, hist, actualizado) para calcular percentiles “pc”; cada test completado lo actualiza.
waitlist/{uid} — interés en formación/comunidad.
reporteTokens — creada por el worker v2 para reportes pagos por Stripe (flujo aparte del panel).
Regla práctica: el país del estudiante (estudiantes.pais) es lo que decide el catálogo; si está vacío o “no especificado” se usa el general.
11. Roles, permisos y navegación
El rol vive en usuarios/{uid}.rol y se copia a session al iniciar sesión. initApp muestra u oculta bloques del sidebar según rol (#admin-nav para admin, #inst-nav para institución/admin) y según plan (canUse('feature') y showUpgradeModal). Un orientador nuevo queda estado:'aprobado' y plan:'free' de entrada; el admin cambia planes desde “Usuarios & Planes” (cambiarPlanOrientador). El estudiante que se registra solo (doRegisterFree) obtiene un usuario con rol:'estudiante' y un doc en estudiantes propio.
Importante: hoy toda esta autorización es del lado del cliente. Lo que realmente protege los datos son las reglas de Firestore, que hay que mantener alineadas con estos roles (ver sección 14).
12. Flujos que conviene conocer antes de tocar código
Arranque. El Script 4 lee la URL. Si hay ?test=&token= cierra sesión (salvo &self=1, vista previa del orientador) y llama a initTestView(sid, token), que lee el doc, compara el token, y si tokenUsado muestra “test ya realizado”. Si no hay parámetros, onAuthStateChanged decide entre login y initApp.
Test. renderTestStep dibuja el paso actual según pasos(); las respuestas van a testState.answers (Likert), testState.cog, testState.raven, testState.pares, testState.valores, testState.islaTop. Cada cambio llama a guardarProgreso. Los mensajes de aliento se muestran entre secciones (mostrarTransicion). Para cambiar preguntas, editar TEST_STEPS: los ids de ítems (R1, I3, LI2, B5…) son los que usan calcRIASEC, calcGardner, calcBigFive; si se agrega un ítem hay que respetar el prefijo.
Informe. Tres renderizados distintos comparten los datos de resultado: showTestSuccess (estudiante al terminar), renderMiResultado (estudiante autónomo en su panel), openInforme (orientador) y downloadPDF (jsPDF, dibuja a mano). Un cambio de contenido normalmente se hace en generarResultadoLocal (datos) y se refleja solo; un cambio visual hay que replicarlo.
Regenerar un informe. resetearTest deja tokenUsado:false, resultado:null y el estudiante vuelve a hacer el test; no hay recálculo a partir de respuestas guardadas porque las respuestas crudas no se persisten en Firestore (solo en localStorage mientras dura el test). Si se quisiera “recalcular con el algoritmo nuevo”, primero habría que guardar answers en el doc al completar.
PDF. downloadPDF(sid) descuenta pdfCount y está detrás del plan (exportPDF). El texto se dibuja con doc2.text y checkY para saltos de página; no usa HTML.
Workers. worker-gps-v2.js: POST /register (alta + Mailchimp), POST /tag-complete (tag “Test GPS completado” + email de resultados), POST /webhook-stripe (verifica firma, crea reporteTokens, envía email con Resend), GET /reporte/:token (HTML del reporte pago). Usa env.* para secretos. worker_registro.js es la versión anterior con claves incrustadas (ver Seguridad). El desplegado en Cloudflare debería ser el v2; conviene confirmarlo en el dashboard de Cloudflare.
13. Convenciones para hacer cambios
Editar en el editor web de GitHub o en local con push a main; Netlify publica en 1–2 minutos. Antes de commitear un cambio de JS, verificar la sintaxis (por ejemplo, abrir la app en local con un servidor estático o pegar el bloque en la consola); un error de sintaxis en el Script 3 deja la app en el login por el window.onerror.
Usar los wrappers de Firestore del Script 2 y escapeHTML() para cualquier texto que venga de un usuario y se inserte con innerHTML. Toda función que se llame desde onclick en el HTML debe exponerse en window (window.miFuncion = miFuncion) porque el Script 3 no es un módulo pero varias funciones se declaran dentro de bloques. Para agregar una página al panel: un <div id="page-x" class="page">, un botón nav-x con onclick="showPage('x')", la entrada en navMap y, si carga datos, un if (id==='x') renderX(); dentro de showPage.
Para cambios de algoritmo, correr la batería de la sección 5 antes y después. Para cambios de catálogo, controlar que CATALOGO_BO() mantenga vectores únicos. Documentar cada deploy en el proyecto (estado-implementacion).
14. Seguridad — estado y prioridades
Resumen de la revisión de septiembre 2026 (detalle en el documento auditoria-seguridad).
- Reglas de Firestore (crítico). El estudiante hace el test sin autenticarse y lee y escribe
estudiantes/{sid}directamente; los workers acceden por REST solo con la API key pública. Eso implica que las reglas permiten lectura y escritura anónima, al menos sobreestudiantes: cualquiera que conozca la URL del proyecto puede listar nombres, edades, emails y resultados de menores, modificarlos o borrarlos. Arreglo recomendado: autenticación anónima para el estudiante (signInAnonymously) y reglas que impidan listar, permitan leer un doc solo por id, y permitan actualizar únicamenteresultado,tokenUsado,abVariante,testIniciadoAtcuandotokenUsadoerafalse;usuarioslegible/escribible solo por el propio uid (sin poder cambiarrol,plan,estado) y por admin;config,baremosywaitlistcon reglas explícitas. - Secretos en el repo (alto).
worker_registro.jstiene la API key de Mailchimp incrustada. Rotarla en Mailchimp, borrar el archivo del repo (queda en el historial: considerar limpiar) y usar solo variables de entorno del Worker, como ya hace v2. - Endpoints abiertos (alto).
/registery/tag-completeaceptan cualquier origen (Access-Control-Allow-Origin: *) sin autenticación ni límite: permiten inscribir emails ajenos en la audiencia, disparar emails y marcar tests como completados por email. Agregar verificación de origen, un secreto compartido o Turnstile, y rate limiting de Cloudflare. - XSS almacenado (medio). Hay ~40 inserciones con
innerHTMLy una sola llamada aescapeHTML. Nombres, escuela, observaciones y notas escritas por un usuario se renderizan sin escapar en paneles de otros usuarios (incluido el admin). Escapar sistemáticamente. - Autorización solo en cliente (medio).
rol,planyestadose leen del doc del usuario y el panel permiteupdateDoc(usuarios/{uid})para el perfil; si las reglas no restringen campos, un usuario puede auto-asignarseadminopremium. Se resuelve con las reglas del punto 1. - Cabeceras de seguridad (medio). No hay
_headersninetlify.toml: agregarContent-Security-Policy,X-Frame-Options,Referrer-Policy,Strict-Transport-Security,Permissions-Policy. - Endurecimiento de Firebase (bajo). Restringir la API key por referrer HTTP en Google Cloud, activar App Check, y revisar los dominios autorizados de Auth.
- Datos de menores. Existe consentimiento en el test y borrado por el orientador; falta política de privacidad visible, plazo de retención y un procedimiento de baja/exportación. Conviene resolverlo antes del piloto en un colegio.
Parte C — Módulo Orientación Laboral
15. Módulo Orientación Laboral (v3.2, 30/09/2026)
Qué es. Un módulo separado del test vocacional, pensado para orientadores laborales (servicios de empleo y formación profesional) y para personas que buscan trabajo por su cuenta, se reinsertan o cambian de rubro. Tiene una página de inicio y cinco herramientas: CV adaptado al aviso, Simulación de entrevista por sector (10 sectores), Traducir experiencia a competencias, Diagnóstico de empleabilidad (16 ítems Likert en 4 dimensiones, 100% determinístico) y Cómo contar el hueco en el CV. Cada una muestra una nota “Para el orientador” y el aviso “El resultado es un borrador para trabajar con la persona, no un producto final”. El resultado es un textarea editable con Copiar, Descargar PDF (jsPDF) y Guardar.
Dónde está en el código.
| Pieza | Ubicación | Notas |
|---|---|---|
| Sidebar | #laboral-nav (después de “Recursos”) | Visible para todos los roles (orientador, institución, admin, estudiante autónomo). Botones nav-laboral*. |
| Páginas | #page-laboral, #page-laboral-cv, -entrevista, -competencias, -diagnostico, -hueco (al final de <main>) | Contenedores vacíos (#lab-body-*); el HTML lo dibuja JS. |
| Navegación | showPage (Script 3) | Entradas nuevas en navMap y if (id.indexOf('laboral')===0) renderLaboralPage(id) dentro de un try/catch. |
| Lógica | Script 5, bloque <script> propio después del Script 4, envuelto en una IIFE | Un error acá no rompe el Script 3. Expone en window: iaLaboral, renderLaboralPage y las funciones lab* de los onclick. Estilos propios (.lab-*) inyectados por labCSS() con las variables CSS de la app. Las herramientas se dibujan una sola vez por carga (no se pierde lo escrito al navegar); el inicio se redibuja siempre para refrescar la lista. |
| Prompts | LAB_SYS_BASE + LAB_SYS.cv|entrevista|competencias|hueco | Español rioplatense con voseo, lenguaje simple, sin inventar datos (lo que falta va como [completar: …]), sin datos sensibles, texto plano (sin markdown; limpiarSalida igual limpia asteriscos y numerales). |
| Contenido fijo | LAB_PREG_GENERALES, LAB_PREG_SECTOR, LAB_DIM, LAB_ACCIONES, LAB_MAPA_COMP, LAB_HUECO_TXT | Preguntas de entrevista por sector, ítems y plan del diagnóstico, y plantillas de respaldo. Se editan sin tocar la lógica. |
IA: iaLaboral(systemPrompt, userPrompt) y netlify/functions/ia.js. iaLaboral hace POST /.netlify/functions/ia (mismo origen) con {system, messages} y el token de Firebase del usuario en Authorization: Bearer; devuelve el texto. La función ia.js (Netlify Functions v2, Node 18+, ES module gracias a netlify/functions/package.json con "type":"module") valida: solo POST, Origin igual al host (sin cabeceras CORS, así que otro origen queda bloqueado), JSON de hasta 40.000 caracteres, sistema ≤ 10.000, hasta 20 mensajes, y que el token de Firebase sea válido (consulta identitytoolkit accounts:lookup con la API key pública). Después llama a https://api.anthropic.com/v1/messages con model: "claude-sonnet-4-6", max_tokens: 2000, cabeceras x-api-key y anthropic-version: 2023-06-01, timeout 55 s. Devuelve {text} o {code, error} con mensajes amigables (sin exponer la clave). Tiene además rateLimit de Netlify: 20 pedidos por minuto por IP. El worker de Cloudflare y la función vieja llamarIA no se usan en este módulo.
Variable de entorno ANTHROPIC_API_KEY. Se crea en Netlify → Site configuration → Environment variables (alcance: Functions). Sin ella, la función responde 503 {code:'no_key'} y la UI muestra “La IA todavía no está configurada” y arma igual un borrador base con plantilla (CV, competencias, hueco, devolución de entrevista). El diagnóstico nunca usa IA. Opcionales: ALLOWED_ORIGINS (orígenes extra separados por coma) y FIREBASE_WEB_API_KEY.
Colección laboral (Firestore). Un documento por resultado guardado, id 'l' + uuid: uid (dueño), herramienta (cv | entrevista | competencias | diagnostico | hueco), fecha (ISO), entrada (objeto con lo que se cargó en el formulario), salida (texto final, ya editado), consultante (texto libre opcional) y actualizado cuando se guardan cambios. Se lee con where('uid','==',session.uid) y se ordena en el cliente (no requiere índice). No toca estudiantes ni usuarios. Necesita reglas propias: cada usuario autenticado crea, lee, edita y borra solo los suyos (ver CAMBIOS.md). Si las reglas no lo permiten, la app avisa “No se pudo guardar” y el resto sigue funcionando.
Privacidad. Los textos que se mandan a la IA salen del navegador hacia Anthropic vía la función; por eso los formularios piden solo nombre o iniciales del consultante y advierten no cargar documento ni diagnósticos. Pendiente: mencionar este uso en la política de privacidad.
Cómo probar. Sin IA: Diagnóstico completo (16 respuestas → barras, freno principal y plan de 4 semanas) y cualquier otra herramienta (muestra el aviso de IA no configurada + plantilla). Con IA: usar un aviso real y verificar que no aparezcan datos inventados. Guardar, ver en “Mis trabajos laborales”, descargar PDF y borrar.
Parte D — Orden de las actividades del test: criterios y evidencia
16. Orden recomendado (revisión de 5 expertos, 22/10/2026)
El 22/10/2026 se pidió a cinco revisores (psicometrista, psicólogo vocacional Holland/SCCT, metodólogo de encuestas, orientadora escolar y especialista en UX para adolescentes) que evaluaran el orden de las partes del test solo con evidencia verificable. Ninguno encontró un estudio que compare órdenes de secciones en un test vocacional para adolescentes en celular: todo lo que sigue es evidencia indirecta (pruebas de rendimiento como PISA, metodología de encuestas, diseño de inventarios clásicos y teoría vocacional). Por eso cada criterio lleva su nivel de confianza.
Dato propio de partida. A/B del orden (A: torneo cerca del final; B: torneo primero), tests reales al 22/10: A 185 de 199 terminaron (93 %), B 191 de 207 (92 %). Con ~200 por variante el margen es de ±5 puntos: el empate no prueba que el orden dé igual, solo que no cambia la finalización. La calidad de las respuestas no se midió.
Orden recomendado — implementado el 22/10/2026 como "orden C" para todos los tests nuevos (campo abVariante: 'C'); quien había empezado con el orden A o B lo termina con ese orden.
- Bienvenida, consentimiento y punto de partida. Explicar qué mide cada parte y para qué se usa el informe (NCDA, Código de Ética 2024, E.3.a). Preguntas fáciles primero (Dillman). No preguntar género antes de los ejercicios.
- Isla ideal. Entrada en calor expresiva, sin nombres de carreras, con una mini‑revelación ("tu isla revela que sos…") como recompensa temprana. Paralelo con los "Daydreams" del Self‑Directed Search de Holland, que abren el cuadernillo.
- Intereses (RIASEC). Es lo que más pesa en el resultado (55 %): se mide temprano, cuando la atención es mejor, y antes de mostrar cualquier carrera.
- Lo que se te da bien (inteligencias autopercibidas). Antes de los ejercicios con respuesta correcta: hacer primero una prueba difícil baja la autoevaluación posterior.
- Pantalla de encuadre y pausa. "No es un examen, no se aprueba ni se reprueba · sirve para ver cómo pensás, no cuánto sabés · es normal sentir nervios y pueden ayudarte · si uno te traba, salteá".
- Ejercicios de razonamiento.
- Matrices con figuras. Cortan la monotonía de las escalas.
- Cómo sos (personalidad) y cómo aprendés. Bloque de menor exigencia; mantiene el ítem de control de atención.
- Valores. Primero qué busca en un trabajo, después elegir carreras (Teoría del Ajuste al Trabajo, Dawis y Lofquist).
- Carreras del futuro.
- Torneo de áreas y carreras. Cierre lúdico justo antes del informe (efecto pico‑final).
Criterios y evidencia
- Carreras después de los intereses (consenso de los 5; confianza moderada). Los títulos ocupacionales arrastran estereotipos de género y prestigio: en 16.484 jóvenes, el sesgo de ítems del Strong correlacionó r = .65 con el "sextipo" de la ocupación (Aros, 1996, tesis, University of North Dakota). El UNIACT de ACT excluye a propósito los títulos y mide actividades (Prediger y Cole, 1975, ACT Research Report 68). El SDS de Holland pone ocupaciones después de actividades y competencias (Dozier, Sampson y Reardon, 2013, The Professional Counselor 3(2)). Por eso la variante B (torneo primero) no se recomienda.
- Razonamiento temprano, no al final (consenso; confianza moderada). En PISA 2009 (n = 467.819) el rendimiento cae 0,17 DE por cada bloque que un ítem se corre hacia atrás (Debeer, Buchholz, Hartig y Janssen, 2014, JEBS 39(6)); también en ciencias y matemática (Wu, Debeer et al., 2019). En Dinamarca los puntajes bajan a medida que avanza el día y una pausa los recupera (Sievertsen, Gino y Piovesan, 2016, PNAS). Matiz: la fatiga subjetiva no siempre baja el rendimiento (Ackerman y Kanfer, 2009, J. Exp. Psych.: Applied 15(2)); pesan también la motivación y el esfuerzo.
- Autopercepción antes de los ejercicios (3 de 5; confianza moderada). Después de una prueba difícil, la inteligencia autoevaluada bajó (ηp² ≈ 0,06–0,07) (Coane et al., 2023, Scientific Reports 13). El psicólogo vocacional prefería las autopercepciones después de los ejercicios para adelantarlos aún más; se decidió por la evidencia experimental de Coane.
- Lo importante primero; la calidad cae aunque la persona termine (consenso; confianza media‑alta). Las preguntas ubicadas tarde se responden más rápido, más cortas y con respuestas más uniformes (Galesic y Bosnjak, 2009, Public Opinion Quarterly 73(2)). El descuido suele empezar a mitad del cuestionario (Welz y Alfons, preprint arXiv 2303.07167).
- Encuadre de los ejercicios (orientadora; confianza media). Decir que los nervios pueden ayudar subió el puntaje de matemática (Jamieson, Mendes, Blackstock y Schmader, 2010, JESP 46(1)); escribir las preocupaciones antes del examen igualó a los estudiantes ansiosos con los tranquilos (Ramirez y Beilock, 2011, Science). La amenaza del estereotipo existe pero es chica en contextos reales (Flore y Wicherts, 2015, J. School Psychology 53(1)): por precaución, no preguntar género antes de los ejercicios.
- Celular y adolescentes (UX y metodólogo; confianza media). En celular, el abandono baja con cuestionarios más cortos, optimizados y menos complejos (Couper y Mavletova, 2015, meta‑análisis en Mobile Research Methods); un ítem por pantalla reduce las respuestas uniformes frente a las grillas (Revilla, Toninelli y Ochoa, 2015, RECSM WP46). En 11–16 años el riesgo principal es el aburrimiento (Borgers, de Leeuw y Hox, 2000). La gamificación tiene efectos modestos y dependientes del contexto (Hamari, Koivisto y Sarsa, 2014; Sailer y Homner, 2020).
- Barra de progreso (UX; confianza media). Una barra que avanza rápido al principio reduce el abandono; una que arranca lenta lo aumenta (Villar, Callegaro y Yang, 2013, meta‑análisis de 32 experimentos). Conviene que avance por partes y que los primeros tramos sean cortos.
- Cierre (UX y orientadora; evidencia indirecta). El recuerdo de una experiencia depende del momento más intenso y del final (Kahneman, Fredrickson, Schreiber y Redelmeier, 1993, Psychological Science 4(6)): terminar con algo motivador (el torneo), nunca con ejercicios.
Desacuerdos que quedaron abiertos
- Dónde poner la isla: al principio (expresar antes de evaluar, como el SDS) o al final (pico‑final). Se eligió el principio porque el torneo cumple el rol de cierre.
- El psicometrista proponía razonamiento y matrices antes que los intereses, para protegerlos aún más de la fatiga. Se priorizó medir intereses (55 % del resultado) en limpio y temprano.
- El metodólogo sugería usar el torneo como pausa antes de personalidad. Se mantuvo al final por la regla "valores antes de elegir carreras".
Cómo medir si el orden funciona (finalización sola no alcanza; Leiner, 2019, Survey Research Methods): abandono por parte; segundos por ítem y porcentaje de respuestas demasiado rápidas; respuestas siempre iguales dentro de cada bloque; ítem de control de atención; fiabilidad de cada escala; aciertos en razonamiento y matrices según la posición del bloque; y una pregunta de satisfacción al final. Prueba directa del sesgo de los títulos con datos propios: comparar la diferenciación del perfil RIASEC entre la variante A y la B.
Al cambiar el orden. El orden forma parte de la administración estandarizada: los percentiles (baremos RIASEC, Big Five y razonamiento) se calcularon con el orden anterior. Conviene marcar los tests con la versión del orden y recalcular los baremos cuando haya suficientes casos nuevos. Quien ya empezó el test con un orden lo termina con ese orden.
Fuentes (verificadas por los revisores): Debeer et al. 2014 · Wu et al. 2019 · Coane et al. 2023 · Galesic y Bosnjak 2009 · Aros 1996 · Prediger y Cole 1975 · Dozier et al. 2013 · Sievertsen et al. 2016 · Jamieson et al. 2010 · Flore y Wicherts 2015 · Couper y Mavletova 2015 · Revilla et al. 2015 · Villar et al. 2013 · Kahneman et al. 1993 · Leiner 2019 · NCDA Code of Ethics 2024
17. Pruebas adaptadas a la edad (22/10/2026)
Desde el 22/10/2026 los ejercicios de razonamiento y las matrices tienen tres versiones según la edad de la ficha, alineadas con las franjas del baremo de razonamiento (14‑15, 16‑18, 19+). Cada versión se compara solo con su franja; en el informe del orientador figura qué versión hizo el estudiante (campo resultado.nivelPruebas).
- Inicial (hasta 15 años): 6 de las 20 preguntas se cambian por equivalentes más simples, de las mismas áreas (porcentajes redondos, vocabulario cotidiano, series aditivas, conectores de causa). En las matrices, las dos de dos reglas combinadas se cambian por dos de una sola regla, al principio.
- Estándar (16 a 18 años): la versión de siempre.
- Avanzada (19 años o más): 6 preguntas se cambian por otras más exigentes (aumentos y descuentos encadenados, proporción inversa, vocabulario culto, silogismo con negación, Fibonacci). Las dos matrices más fáciles se cambian por dos de tres reglas a la vez, al final.
Sin edad cargada se usa la estándar. El test gratis no tiene ejercicios de razonamiento. Al tener ~50 tests por versión, resembrar el baremo de razonamiento (sembrarBaremoRaz()) para que cada franja se compare con su propia versión.
18. Modo mantenimiento, link corto y tiempos por parte
Modo mantenimiento (Configuración del sistema, solo admin): mientras está activo nadie puede empezar un test nuevo (ficha, link de clase o test gratis); quien ya lo estaba haciendo puede terminarlo. El panel muestra cuántos tests están en curso en las últimas 3 horas. Regla: publicar cambios solo sin estudiantes activos, de preferencia de noche o el fin de semana. Se guarda en config/mantenimiento.
Link corto: las fichas nuevas tienen un código de 10 caracteres y su link es gpsvocacionalapp.netlify.app/t/CÓDIGO. Al abrirlo, la app busca la ficha y redirige al link completo. Las fichas anteriores siguen con su link largo.
Tiempos por parte: cada test guarda en resultado.tiemposPasos los segundos de cada parte. Sirve para medir el orden C (tiempos muy cortos, abandono por parte).
19. Sesiones y técnicas manuales con ayuda de IA (22/10/2026)
Desde "Mis estudiantes" (botón 📝 Sesiones) o desde el informe, la orientadora registra cada sesión: fecha, técnica (frases incompletas, collage, línea de vida, genograma ocupacional, dibujo, fantasía guiada, autobiografía, escudo personal, tarjetas de ocupaciones, entrevista a un profesional, cuestionario en papel, entrevista u otra), sus notas y hasta 3 fotos. Las fotos se comprimen en el navegador (JPEG, máx. 1280 px, ~250 KB) antes de guardarse. Funciona aunque el estudiante todavía no haya hecho el test.
Ayuda con IA (lee el resumen del test y hasta 10 sesiones con hasta 6 fotos): Planificar la próxima sesión (objetivo, técnica con consigna, preguntas, qué observar), Ver patrones (coincidencias y tensiones entre test y técnicas, temas recurrentes, nivel de confianza) y Armar la devolución (texto para el estudiante + notas para la orientadora). Todo es borrador: hipótesis de trabajo con la fuente de cada observación, sin etiquetas clínicas; ante señales de malestar sugiere derivar. Se puede copiar o bajar en PDF con el logo.
Datos: colección sesiones {sid, orientadorId, autorUid, fecha, tecnica, notas, fotos[], creada}; solo la lee la orientadora dueña de la ficha y la admin (reglas en firestore.rules). La IA usa netlify/functions/ia.js, que ahora acepta bloques de texto e imágenes (máx. 6 fotos). Requisitos: reglas de Firestore publicadas y ANTHROPIC_API_KEY cargada en Netlify. Recomendación: no fotografiar caras ni nombres completos.
| Orientador | Institución/Consultorio | Estado | Acciones | |
|---|---|---|---|---|
| Cargando... | ||||