Rastrum

Tareas detalladas

Desglose por ítem del roadmap. Sub-tareas con estado y referencias a specs.

Actualizado: 2026-05-12 · Ver roadmap

✓ Hecho ~ En curso ○ Pendiente ! Bloqueado

v0.1 Alpha MVP (online-first) Lanzado

astro-skeleton Esqueleto Astro + Tailwind + i18n
6/6

subtareas

Proyecto Astro 5 con output: 'static'
Tailwind 3 + integración @astrojs/tailwind
Locales EN/ES vía astro/i18n + prefixDefaultLocale
Shells compartidos BaseLayout + DocLayout
Header + Footer con toggle de tema, switcher de idioma y menú móvil
Sitemap (@astrojs/sitemap)
supabase-schema Esquema Supabase con PostGIS + RLS

02-observation.md · 05-map.md · 06-darwin-core.md · 07-licensing.md

8/8

subtareas

6 tablas principales
Columnas geography PostGIS + índices GIST
RLS dueño + lectura pública + credentialed
3 triggers
helper obscure_point() + enum obscure_level
Idempotente — reaplicable con make db-apply
Bucket de storage 'media' + políticas
Permisos de rol (anon SELECT, authenticated CRUD)
auth-magic-link Auth con enlace mágico + modo invitado

04-auth.md

6/7

subtareas

Cliente singleton supabase.ts
Helpers en auth.ts
Páginas de sign-in en EN y ES
Callback maneja flujos PKCE e implicit
Dropdown de avatar en Header con onAuthStateChange
Unión discriminada ObserverRef
Nudge tras la 3ra observación de invitado
auth-multi OAuth Google + GitHub, código OTP, passkey, cerrar sesión en todos los dispositivos

04-auth.md

6/7

subtareas

signInWithGoogle / signInWithGitHub
Flujo de código OTP (verifyOtp)
Passkey enrol + verify
Detección WebAuthn
signOutEverywhere() revocación global
Componente SignInForm compartido
!
Operador: habilitar toggle MFA WebAuthn

Bloqueado por: Paso manual en dashboard

ci-cd CI/CD con GitHub Actions

infra/testing.md · infra/github-actions.yml

5/5

subtareas

ci.yml — typecheck + test + build en PRs
deploy.yml — typecheck + test + build + deploy
deploy-functions.yml — deploys manuales
Secrets PUBLIC_* vía gh secret set
Job de Vitest corre en CI
profile-basics Página de perfil + edición + dropdown de avatar

08-profile-activity-gamification.md

7/7

subtareas

ALTER TABLE users con 11 columnas
Componentes compartidos ProfileView + ProfileEditForm
Páginas en EN /profile/* y ES /perfil/*
Avatar de respaldo con iniciales SVG
Dropdown de avatar (Ver / Editar / Cerrar)
Sección feed de actividad
Sección de insignias
gps-observation Formulario de observación GPS con auto-relleno EXIF

02-observation.md

9/9

subtareas

Captura/carga de múltiples imágenes
GPS en vivo con getCurrentPosition
Fallback GPS EXIF con exifr
Entrada manual de coordenadas
Dropdowns hábitat / clima / tipo evidencia
Textarea de notas (≤2000)
Aviso de privacidad NOM-059/CITES
Captura de audio (MediaRecorder ≤30s)
Guarda en outbox Dexie + intento de sync
plantnet-id Integración con PlantNet

01-photo-id.md · 13-identifier-registry.md

7/7

subtareas

Edge Function con cascada PlantNet → Claude
Campo force_provider para enrutamiento
Soporte de BYO key client_keys.plantnet
Umbral de confianza 0.7
Wrapper en identifiers/plantnet.ts
Probe testConnection()
Operador: deploy + secret
claude-haiku-id Cascada con Claude Haiku 4.5

01-photo-id.md

4/5

subtareas

Prompt de sistema cacheado
Parseo JSON con remoción de fences
Soporte BYO client_anthropic_key
Probe de costo en testConnection
Operador: secret opcional
map-view Mapa MapLibre con pines agrupados

05-map.md

6/6

subtareas

Estilo OpenFreeMap Liberty
Fuente GeoJSON desde la tabla observations
Clustering en zoom bajo + pines en zoom alto
Pines coloreados por reino
Click en pin → popup con miniatura
Capa pmtiles offline (entregada a offline-maps v0.3)

→ rastreado en offline-maps

darwin-core-csv Exportación CSV Darwin Core

06-darwin-core.md

5/5

subtareas

Mapeo + generación CSV en darwin-core.ts
Tres presets: DwC / SNIB / CONANP
Manejo del tier de oscurecimiento
Componente ExportView con selector
9 tests unitarios Vitest
pwa-manifest Manifest PWA + service worker con caché de shell

03-offline.md

4/4

subtareas

manifest.webmanifest con display:standalone
sw.js cache-first para GETs same-origin
Registro solo en producción
Meta tags Apple mobile
offline-queue Outbox Dexie + motor de sync + trigger de identificación

03-offline.md · 10-media-storage.md

6/6

subtareas

RastrumDB con tres tablas
syncOutbox + registerSyncTriggers
Doble path R2 + Supabase Storage
Resize de imagen en cliente
Integración con cascade engine
Soporte para blobs de audio
unit-tests Andamiaje de tests unitarios Vitest

infra/testing.md

4/6

subtareas

Vitest 4.1 + entorno happy-dom
darwin-core.test.ts (9 tests)
byo-keys.test.ts (10 tests)
Targets de Make + integración CI
Suite pgTAP de RLS
Suite Playwright E2E

v0.3 Inteligencia offline + actividad Lanzado

activity-feed Feed de actividad + triggers del servidor

08-profile-activity-gamification.md

6/6

subtareas

Tabla activity_events con enum (12 kinds)
RLS self/público + tiers de visibilidad
Trigger fire_observation_created
Trigger fire_research_grade
Renderizado en perfil con labels i18n
Auto-marca como leído al ver
unread-badge Contador de no leídos en avatar

08-profile-activity-gamification.md

4/6

subtareas

Punto rojo con contador
Refresca con onAuthStateChange
Texto 99+
RPC o query cliente para contar activity_events no leidos
Animacion CSS pulse al llegar nuevo no leido
aria-label accesible anunciando conteo a lectores de pantalla
sensitive-privacy Aviso de oscurecimiento NOM-059 / CITES

02-observation.md

4/5

subtareas

Aviso ámbar siempre visible
Enum obscure_level en el schema
Trigger aplica oscurecimiento
Columna location_obscured separada
Look-up por especie al enviar
exif-extraction Auto-extracción de metadatos EXIF/XMP/ID3

02-observation.md

3/7

subtareas

exifr integrado en formulario
GPS extraído con flag EXIF
DateTimeOriginal → timestamp
Parseo XMP sidecar para metadatos de dron/camara trampa
Extraccion de tags ID3 de grabaciones de audio
Marca/modelo de camara almacenado en media_files
Auto-rotacion por tag de orientacion antes de subir
webllm-text WebLLM Llama-3.2-1B para traducción + notas

11-in-browser-ai.md

5/6

subtareas

@mlc-ai/web-llm instalado
loadTextEngine + translateNote + generateFieldNote
Card de descarga con modal de consentimiento
Gestión de caché (probe / delete / re-download)
requestPersistentStorage() contra evicción iOS
Botones Translate + Auto-narrativa en form
onnx-base Fallback ONNX base EfficientNet-Lite0 (~18 MB INT8 alojado en R2)

13-identifier-registry.md

6/6

subtareas

Convertir modelo a ONNX (TF Model Garden EfficientNet-Lite0)
Bundle de labels ImageNet JSON
Implementación src/lib/identifiers/onnx-base.ts
Preprocesamiento (cover crop 224x224 + media/std ImageNet)
Inferencia en dispositivo vía onnxruntime-web (cacheada en IndexedDB)
Card de descarga en perfil con consentimiento + gestión de caché
offline-maps Descarga de mapas offline pmtiles (México zoom 0–10, ~48 MB)

05-map.md

5/6

subtareas

Archivo pmtiles delimitado a México alojado en R2
Variable PUBLIC_PMTILES_MX_URL conectada al build
Descarga src/lib/offline-map.ts + persistencia Cache API
Protocolo MapLibre pmtiles:// registrado para lecturas offline
Card de descarga en perfil con aviso de conexión móvil
Chunks zoom 11–14 por región bajo demanda
byo-anthropic-key API key de Anthropic propia (en el cliente, nunca persistida)

01-photo-id.md · 13-identifier-registry.md

5/5

subtareas

Store central byo-keys.ts respaldado en localStorage
client_keys.anthropic se reenvía por llamada a la Edge Function
La Edge Function nunca registra ni persiste la clave
UI Configure en Perfil/Editar con Save/Test/Clear
Probe testConnection() verifica una clave activa
webllm-default WebLLM como fallback de IA por defecto (advertencia de descarga en primer uso)

11-in-browser-ai.md · 13-identifier-registry.md

5/5

subtareas

Plugin Phi-3.5-vision registrado en bootstrapIdentifiers()
La cascada cae a WebLLM cuando los plugins de red fallan
Diálogo de descarga en primer uso con tamaño + consentimiento de persistencia
Gestión de caché en perfil (probe/delete/re-download)
Cap de confianza 0.35 para evitar auto-promote
identification-block Bloque de identificación visible en formulario

02-observation.md · 01-photo-id.md

5/5

subtareas

Bloque inline bajo la grilla de fotos (spinner + chip)
Auto-dispara en la primera foto + botón de re-correr
Entrada manual de nombre científico sobreescribe cascada
Confianza y fuente visibles (PlantNet, Claude, …)
Aviso ámbar de baja confianza cuando conf < 0.4
gps-two-pass GPS en dos pasos: rápido aproximado luego alta precisión

02-observation.md

5/5

subtareas

Primera llamada: enableHighAccuracy=false, timeout ~1 s
Segunda llamada: enableHighAccuracy=true, timeout ~10 s
Vista previa del formulario se actualiza al mejorar fix
Lógica de cancelación al enviar / reset del formulario
Cae a GPS EXIF si hay timeout

v0.5 Beta

byo-keys-platform API keys propias por plugin con guía de configuración

13-identifier-registry.md

6/6

subtareas

Contrato KeySpec + SetupStep + testConnection
Store central byo-keys.ts
Migración one-time del legacy
UI Configure por plugin
Botones Save + Test + Clear
10 tests unitarios
webllm-vision ID de respaldo con WebLLM Phi-3.5-vision

11-in-browser-ai.md

5/5

subtareas

Plugin Phi-3.5-vision
Cap duro de 0.35
Disclaimer en perfil
Mismo flujo consent + caché
Integración como respaldo opt-in
discovery-badges 39 insignias semilla + evaluador nocturno

08-profile-activity-gamification.md

6/7

subtareas

Tablas badges + user_badges con RLS
39 insignias (5 categorías × 4 tiers)
Funciones SQL de elegibilidad
Edge Function award-badges nocturna
Pills por tier en el perfil
Cron 30 7 * * *
Integración con feed de actividad
quality-gates Compuerta de calidad: research-grade requiere confianza ≥ 0.4

08-profile-activity-gamification.md

2/6

subtareas

Trigger enforce_research_grade_quality
Cap confidence_ceiling por plugin
Banner de advertencia cuando confianza < 0.4
Widget admin mostrando tasa de rechazo research-grade
Cobertura Vitest para casos borde del trigger
Documentacion en spec del modulo sobre umbrales y justificacion
consensus-workflow Consenso 2/3 + anti-sybil + peso experto

08-profile-activity-gamification.md

2/4

subtareas

Trigger prevent_self_validation
recompute_consensus con peso 3× experto
UI de validador para la comunidad
Trigger auto-recompute
multi-image Observaciones multi-imagen

02-observation.md

4/4

subtareas

Galería <input multiple>
Grid con primary + remove
Blobs guardados + subidos a R2
media_files con sort_order + is_primary
eco-evidence Campos de evidencia ecológica

02-observation.md

3/6

subtareas

Enum evidence_type en observations
Dropdown con 9 valores
Labels i18n para los 9 valores del enum en EN + ES
Mapeo Darwin Core de evidence_type a basisOfRecord
Filtro por evidence_type en grilla /explore/recent
Set de iconos por tipo de evidencia en vista de detalle
birdnet-audio ID de audio con BirdNET-Lite (Cornell Lab CC BY-NC-SA 4.0, ONNX en R2)

12-birdnet-audio.md

9/9

subtareas

Captura de audio en formulario (≤30s)
Motor de sync enruta audio a R2 + cascade
Spec módulo 12 escrito
BirdNET-Lite v2.4 ONNX en R2 + PUBLIC_BIRDNET_WEIGHTS_URL
Decodificación de audio + remuestreo 48 kHz + ventana 3 s
Inferencia onnxruntime-web en el plugin (top-K especies)
Bundle de labels JSON (~6,000 especies)
Card de descarga en perfil con consentimiento + caché
Atribución Cornell Lab CC BY-NC-SA 4.0 en UI + export DwC
scout-v0 Rastrum Scout v0 (ID conversacional, RAG con pgvector)
0/6

subtareas

!
Habilitar extensión pgvector

Bloqueado por: Diferido en future-migrations.md

!
Pipeline de embeddings (Voyage-3 ~50K taxa)

Bloqueado por: Presupuesto de embeddings

Tabla taxon_embeddings + índice HNSW
Edge Function scout
UI de chat en la app
Tabla chat_sessions + historial
onnx-regional Paquetes ONNX regionales (Oaxaca, Yucatán)
0/5

subtareas

!
Pipeline de entrenamiento

Bloqueado por: Infraestructura ML

Set de entrenamiento por región
Convertir a ONNX int8
Cards de descarga en perfil
Routing del cascade por región
gbif-ipt Publicación piloto GBIF IPT (DwC-A ZIP)

06-darwin-core.md

3/6

subtareas

!
Solicitud de cuenta de publicador GBIF

Bloqueado por: ~2 semanas de revisión

Generador de DwC-A ZIP
Edge Function export-dwca
Script publish-to-ipt.sh para operadores
Edge Function gbif-publish (cron mensual)
Tracking de DOIs en dataset_versions
local-contexts Integración con Avisos BC/TK de Local Contexts
0/4

subtareas

!
Consentimiento comunitario

Bloqueado por: Trabajo de gobernanza multi-mes

Integración con Local Contexts Hub API v2
Tabla de enlace observation_bc_notices
Renderizado de etiquetas BC/TK
user-api-tokens Tokens de API de usuario (rst_*, con scopes, hash SHA-256)

14-user-api-tokens.md

5/5

subtareas

Tabla user_api_tokens con hash + scopes[]
Generación crypto-strong rst_<base32> en cliente
RLS solo-dueño en user_api_tokens
Texto plano mostrado al usuario una sola vez
Actualización fire-and-forget de last_used_at
token-rest-api API REST autenticada por token

14-user-api-tokens.md

7/7

subtareas

Edge Function supabase/functions/api desplegada
/api/observe POST crea una fila en observations
/api/identify POST corre la cascada con BYO keys del usuario
/api/observations GET (paginado, RLS por usuario)
/api/export GET devuelve CSV Darwin Core
Aplicación de scopes por ruta
Desplegada --no-verify-jwt; la función valida rst_*
token-ui UI de gestión de tokens en /profile/tokens (EN) y /perfil/tokens (ES)

14-user-api-tokens.md

6/6

subtareas

Rutas locale /profile/tokens y /perfil/tokens
Form de creación + modal de texto plano
Lista de tokens existentes
Acción de revocación con confirm
Strings i18n bajo tr.profile.tokens.*
Botón copiar al portapapeles en creación

v1.0 Lanzamiento Público

streaks Rachas opt-in + ventana de gracia

08-profile-activity-gamification.md

7/8

subtareas

Tabla user_streaks (current_days, longest_days, last_qualifying_day, grace_used_at) + RLS self-read + pública si opt-in
Función PL/pgSQL recompute_streak(p_user_id) con ventana de gracia única por 30 días
Compuerta de calidad: solo identificaciones con confianza ≥ 0.4 cuentan como día válido
Edge Function recompute-streaks — itera usuarios con gamification_opt_in y llama recompute_streak vía RPC
Schedule pg_cron (nocturno 07:05 UTC) dispara la Edge Function vía net.http_post
StreakCard.astro en perfil — CTA de opt-in, current/longest/last-active, estado flama (active/at_risk/broken/none)
Helper summarizeStreak() en src/lib/social.ts deriva estado desde la fila + fecha de hoy
Email de hito de racha (7/30/100 días)
shareable-cards Tarjetas OG para compartir observaciones
7/7

subtareas

Edge Function share-card — acepta ?obs_id=, format=html|svg, hace join observations + identifications + users
Renderer SVG 1200×630 con franja de marca, fondo degradado, nombre de especie en cursiva, meta fecha + región + hábitat
obscure_level='full' devuelve 404 — especies totalmente ocultas no se pueden raspar via share-card
Badge visual de especie sensible cuando obscure_level > 'none' (muestra región, oculta locación precisa)
Página pública src/pages/share/obs/[id].astro con meta OG/Twitter que apuntan al endpoint share-card
Fallback anónimo cuando profile_public=false — observer mostrado como 'Anonymous observer'
Escape XML de todas las cadenas del usuario para evitar inyección en SVG
social-features Esquema seguidores + comentarios + listas

08-profile-activity-gamification.md

8/8

subtareas

public.follows (follower_id, followee_id) PK compuesta + RLS follows_self_manage + follows_public_read
CHECK anti-auto-seguimiento (follower_id ≠ followee_id) en follows
public.observation_comments con parent_id auto-FK (threading de un nivel) + soft-delete vía deleted_at + idx_comments_obs
RLS comentarios: comments_authenticated_insert + comments_self_update + comments_public_read
public.watchlists (user_id, taxon_id, radius_km, region_geojson) + RLS watchlists_self
Comments.astro — lista + composer + reply (un nivel) + editar/borrar propios, límite 2000 caracteres
FollowButton.astro — toggle optimista, oculto cuando target=self, CTA de sign-in cuando RLS rechaza
WatchlistView.astro — feed de activity_events vía RLS visibility=followers/public, marcar-todo-leído
expert-system Peso 3× experto taxonómico

08-profile-activity-gamification.md

6/7

subtareas

Columnas users.is_expert (boolean) + users.expert_taxa (text[] p.ej. ARRAY['Aves','Plantae']) en la tabla users
recompute_consensus(p_observation_id) PL/pgSQL — SUM peso 3.0 cuando u.is_expert AND t.kingdom = ANY(u.expert_taxa) si no 1.0
Tabla public.expert_applications (user_id, taxa[], credentials, institution, orcid, status enum) + índices para cola pendiente
RLS expert_applications — usuario lee/inserta propias, UPDATE solo admin (withdraw vía fila nueva mantiene auditoría)
ExpertApplyView.astro — multi-select de taxa (11 reinos), textarea credentials, validación ORCID, UI estado pending/approved
Atajo experto-ya: ExpertApplyView lee users.is_expert y muestra agradecimiento en vez del formulario
Insignias de experto visibles en página de perfil (badge visual cuando is_expert + chips por reino)
bioblitz-events-schema Tabla de eventos + RLS (esquema)

08-profile-activity-gamification.md

7/7

subtareas

Tabla public.events — id, slug UNIQUE, name, description_md, organiser_id FK→users, starts_at, ends_at, kind
region_geojson geography(Polygon, 4326) NOT NULL — define los límites espaciales de las observaciones
Tres tipos de evento vía CHECK (kind IN ('bioblitz','survey','challenge'))
Índice temporal idx_events_time sobre (starts_at, ends_at)
Índice espacial GIST idx_events_region sobre region_geojson para queries obs-dentro-de-región
RLS habilitada + política events_public_read (cualquiera puede SELECT — eventos públicamente navegables)
Esquema idempotente (CREATE TABLE IF NOT EXISTS, DROP POLICY IF EXISTS … ; CREATE POLICY) — reaplicable con make db-apply
institutional-export Plantillas CSV DwC + SNIB + CONANP

06-darwin-core.md

7/7

subtareas

DWC_COLUMNS — set completo de Darwin Core en src/lib/darwin-core.ts (default toCSV())
Subset SNIB_COLUMNS — 14 columnas alineadas al esquema de ingesta CONABIO SNIB (toCsvSnib())
Subset CONANP_COLUMNS — 9 columnas para formato reporte ANP incluyendo informationWithheld (toCsvConanp())
<select id="format"> en ExportView.astro con opciones dwc / snib / conanp
Switch en handler de descarga elige el serializer correcto según fmt
Sufijos de archivo por formato — rastrum-{dwc|snib|conanp}-YYYY-MM-DD.csv vía downloadCSV()
Tests unitarios para subsets de columnas y orden de cabecera CSV en src/lib/darwin-core.test.ts
credentialed-access Compuerta RLS credentialed_researcher

07-licensing.md

5/6

subtareas

users.credentialed_researcher (boolean DEFAULT false) — set por admin tras verificación, no auto-servicio
users.credentialed_at (timestamptz) + users.credentialed_by (uuid FK→users) para auditoría
Política RLS obs_credentialed_read — SELECT evita obscure_level cuando credentialed_researcher del caller=true
La política respeta retención por avisos BC/TK (overrides comunitarios siguen vigentes)
ALTER TABLE … ADD COLUMN IF NOT EXISTS idempotente para las tres columnas
Página de solicitud (UI auto-servicio — admin revisa por dashboard)
env-enrichment Edge Function fase lunar + OpenMeteo
7/7

subtareas

Edge Function supabase/functions/enrich-environment (Deno) — POST { observation_id }, cliente service-role
Algoritmo lunar de Conway — calcula phase ('new'..'waning_crescent') + illumination 0..1 desde observed_at UTC
Fetch histórico OpenMeteo (CC BY 4.0) — precipitation_sum + temperature_2m_mean diarios para lat/lng/date
UPDATE observations SET moon_phase, moon_illumination, precipitation_24h_mm, temp_celsius, post_rain_flag (>5mm) — 5 columnas
Auto-disparo desde src/lib/sync.ts tras sincronizar observación — fire-and-forget (fallos no bloquean el sync)
Idempotente — seguro invocarlo varias veces con el mismo observation_id (semántica UPSERT vía update + PK de observación)
Tolerancia a enriquecimiento parcial — fallo OpenMeteo igual escribe fase lunar; fallo lunar deja columnas NULL pero no devuelve 500
video-support Soporte de video ≤30s (H.265/AV1)

02-observation.md

3/6

subtareas

Captura de video en formulario (MediaRecorder ≤30s)
Fila media_files con media_type='video' + carga R2
Tile de vista previa de video en grilla (autoplay muted)
Pipeline de transcodificación con ffmpeg
Extracción de frames + separación de audio para BirdNET
Reproductor en página share/obs
camera-trap-ingest Ingesta de cámara trampa (MegaDetector v5a YOLOv5 ONNX en el dispositivo)

09-camera-trap.md · 19-batch-photo-importer.md

4/7

subtareas

UI de carga masiva en /perfil/importar/camara-trampa (drag-drop)
Ubicación compartida del trap (GPS único) + timestamp EXIF
Plugin runtime:'client' que corre MegaDetector v5a vía onnxruntime-web (megadetector-yolo.ts letterbox+NMS, megadetector-cache.ts descarga/cache)
FilteredFrameError corta el cascade para cuadros vacíos/humano/vehículo (cascade.ts + errors.ts)
!
El operador aloja el ONNX de ~134 MB en PUBLIC_MEGADETECTOR_WEIGHTS_URL

Bloqueado por: Acción del operador: convertir el checkpoint v5a y subirlo a R2

Tablas camera_trap_deployments + processing_queue (diferidas)
Edge Function: motion → ID → research-grade
capacitor-ios Wrapper iOS Capacitor para App Store (v1.2)
0/5

subtareas

!
Apple Developer Program ($99/año)

Bloqueado por: Suscripción requerida

Andamiaje Capacitor + platform/ios
Plugins nativos
Metadatos + screenshots para App Store
Primer build de TestFlight
follows-comments-ui UI de seguidores + comentarios + listas

08-profile-activity-gamification.md

3/5

subtareas

Botón Seguir en perfiles públicos (FollowButton.astro)
Componente de comentarios anidados (Comments.astro)
Vista de listas en perfil (WatchlistView.astro)
Eventos de seguidos en feed
Alertas de lista (digest, cron diario)
map-location-picker Selector de ubicación con mapa interactivo

15-map-location-picker.md · 02-observation.md

5/5

subtareas

Arrastrar pin + tap para colocar en MapLibre
Búsqueda de localidad con resultados EN/ES
Geocodificación inversa para poblar localidad
UX bottom-sheet móvil para el selector
Integración round-trip con campos del formulario
my-observations Página de historial de observaciones personales

16-my-observations.md

6/6

subtareas

Ruta /profile/observations (EN) / /perfil/observaciones (ES)
Componente MyObservationsView compartido
Grid de miniaturas con pill de estado
Click en fila → /share/obs/<id> para detalle
Incluye filas pendientes de outbox (sin sincronizar)
Paginación + orden más reciente primero
camera-getUserMedia Cámara en app vía getUserMedia con fallback a input de archivo

17-in-app-camera.md · 02-observation.md

5/5

subtareas

getUserMedia con facingMode environment
Preview <video> + captura a canvas → Blob
Permiso denegado → fallback a input file capture=environment
Cleanup de stream al cerrar para evitar indicador
Las superficies Identify + Observe lo usan
batch-exif-importer Importador masivo de fotos con extracción EXIF

19-batch-photo-importer.md

6/6

subtareas

Captura multi-foto por drag-drop / file-input
Extracción exifr de GPS + DateTimeOriginal por archivo
Tabla de revisión por fila (include/exclude, edición)
Insert masivo a outbox Dexie + sync en cola
Páginas EN /profile/import/, ES /perfil/importar/
Deep-link cámara-trampa (subruta) para subidas escalonadas
oauth-custom-domain Dominio personalizado en OAuth de Supabase (auth.rastrum.org)

04-auth.md

0/4

subtareas

!
Plan Supabase Pro ($25/mes) — requerido para dominio de auth custom

Bloqueado por: Diferido por el target de costo cero; el host de callback por defecto sirve para v1.0

CNAME DNS para auth.rastrum.org
Actualizar Site URL + allow-list en Supabase Auth
Re-probar flujos OAuth Google + GitHub en dominio custom
mcp-server Servidor MCP para agentes de IA (JSON-RPC sobre HTTP)

15-mcp-server.md · 14-user-api-tokens.md

6/6

subtareas

Edge Function supabase/functions/mcp con transporte JSON-RPC 2.0
tools/list y tools/call respaldados por scopes de user_api_tokens
5 herramientas: identify_species, submit_observation, list_observations, get_observation, export_darwin_core
initialize + ping sin auth para probing de capacidades
Desplegada --no-verify-jwt; la función valida rst_*
Integraciones documentadas para Claude Desktop, Cursor, VS Code, Copilot Coding Agent
rastrum-org-domain Migrar dominio canónico a rastrum.org
6/6

subtareas

Registrar rastrum.org + DNS vía Cloudflare
Actualizar config de Astro + host del sitemap
Migrar hostname del bucket a media.rastrum.org
Lista de pass-through del SW actualizada
Dominio antiguo redirige 301
Documentación actualizada a rastrum.org
ux-revamp-pr1-ia-chrome Renovación UX PR 1: arquitectura + chrome

00-index.md

8/8

subtareas

Helper chrome-mode + módulo chrome-helpers + routeTree (TDD)
Strings i18n para tagline, dropdown explorar, grupos docs, drawer, barra inferior
Páginas placeholder de subrutas (/explorar/recientes, /explorar/seguimiento, /explorar/especies)
Redirección 301 de /perfil/seguimiento a /explorar/seguimiento
Componentes MegaMenu, MobileBottomBar, MobileDrawer
Reescritura de Header.astro con copy por verbo, layout mobile-friendly, classnames accent-color
BaseLayout pb-20 sm:pb-0 para separación de barra inferior móvil
Cobertura e2e: highlight de rail activo, mega-menu renderiza, FAB apunta a /observar, toggle drawer, 301 watchlist
bioblitz-events-ui Eventos bioblitz

08-profile-activity-gamification.md

0/6

subtareas

Eventos bioblitz — UI (página de detalle, agregados en vivo, insignias de participación)
Pagina de detalle de evento con titulo, descripcion, rango de fechas y poligono en mapa
Conteo en vivo de observaciones y especies agregado del poligono del evento
Insignia de participacion otorgada al completar bioblitz via funcion award-badges
Pagina de listado de eventos en /explore/events con filtros proximo/activo/pasado
Boton unirse a evento con verificacion auth y conteo de participantes

v1.1 Pulido de UX (lluvia de ideas post-lanzamiento) Planeado

chat-improvements-v1-1 Chat v1.1: Gemma 4 texto + contexto por entidad + 5 herramientas tipadas

20-chat

15/15

subtareas

loadGemmaTextEngine: reusar pesos de Gemma 4 E2B vía transformers.js para generación de texto (sin descarga doble)
chat-engine: streaming + bucle de herramienta de 1 ronda, fallback a Llama si falla Gemma
Registro EntitySpec genérico tipo src/lib/identifiers/ — 6 built-ins (observación/especie/proyecto/estación/observador/perfil propio)
5 RPCs SECURITY INVOKER chat_find_* + dispatcher chat_entity_card + 6 builders por tipo
chat-tools.ts: capa tipada de herramientas JSON con validadores manuales (sin Zod), unión discriminada {ok|unknown_tool|invalid_args|network|offline}
Portal de consentimiento de dos botones: Gemma 4 (recomendado) vs Llama 3.2 1B (más ligero) lado a lado; refreshState verifica ambos cachés
AskRastrumButton + parser de enlace directo ?attach=kind:id; montado en share-obs, perfil público, detalle de proyecto, perfil de especie
Popover ChatEntityPicker con 6 pestañas y búsqueda; se abre desde el botón Contexto del compositor
Chips de sugerencia en estado vacío (Cerca de mí, Revisar observación, Cómo funciona, NOM-059) llenan input + auto-envían
Badge de modelo en el header resuelto del lado del cliente desde el estado del caché (Gemma 4 / Llama 1B / En el dispositivo)
Link de Chat agregado al MobileDrawer (faltaba en viewports <sm)
Suite de regresión tests/sql/chat.sql (10 aserciones) + cableado al workflow db-validate
Invariantes de privacidad: coords-obscure en card de observación, solo-self en self_profile_card, sin-centroide en observer_card
Eventos de telemetría en bus rastrum:onboarding-event (chat.entity.attached, chat.tool.called, chat.tool.failed, chat.engine.fallback)
Runbook: docs/runbooks/chat-improvements.md + link en índice
community-validation Cola de validación comunitaria (Módulo 22)

22-community-validation

7/7

subtareas

Spec v1.0 → v1.3 (revisión Copilot × 2 + auditoría de esquema)
SQL: vista validation_queue + 3 políticas RLS + índice UNIQUE + parche desempate en recompute_consensus
Componentes: ValidationQueueView, SuggestIdModal, ValidationDashboardView
4 rutas: /{en,es}/{explore,explorar}/{validate,validar}/ + análogas en perfil
Chip de grado-investigación en MyObservationsView + ExploreRecentView
CTA Sugerir en cada página /share/obs/?id=
Tarjeta Validar en el índice /explorar/
owner-observation-crud CRUD del observador sobre observaciones sincronizadas

02-observation · 07-licensing

4/4

subtareas

Panel Gestionar en share/obs (visible sólo al observador)
Editar notas + obscure_level + sobreescribir nombre científico
Función Edge delete-observation — borrado atómico R2 + DB
Cliente cableado a delete-observation en vez de DELETE directo (sin blobs huérfanos)
og-pipeline Pipeline Open Graph (cero cómputo por request)

10-media-storage

5/5

subtareas

satori + resvg-js para PNGs de páginas estáticas en build
Renderizador cliente en src/lib/og-card.ts (lazy import)
Enganchado a syncOne — tarjeta OG por observación subida junto a la foto
Enganchado a ProfileEditForm — tarjeta OG por perfil al guardar
Mapeo meta-tag en BaseLayout (path → /og/<slug>.png con override)
karma-phase-1-foundation Motor de karma — esquema, cómputo, UI base (Fase 1)

23-karma-expertise-rarity

11/11

subtareas

tablas user_expertise + taxon_rarity + karma_events
taxa.parent_id + trigger ancestor_path + backfill por nombre/rango
shim de migración is_expert → user_expertise
refresh_taxon_rarity() + pg_cron nocturno
helper award_karma()
recompute_consensus invoca award_karma + usa pesos de user_expertise
helpers puros src/lib/karma.ts + Vitest
Microcopy de rareza + magnitudes en modal de Sugerir
Sección de karma + top-5 especialidades en perfil
Página de Pokédex (/profile/dex, /perfil/dex)
Aplicar schema con make db-apply (operador)
ai-sponsorships Patrocinios IA — presupuesto Anthropic patrocinado por operador/comunidad
14/15

subtareas

Schema + RLS (tabla sponsorships, presupuesto mensual USD, políticas de lectura dueño/público)
Helper _shared/sponsorship.ts + pruebas unitarias (verificación de presupuesto, contabilidad de cargos)
Edge Function sponsorships (CRUD + lectura de presupuesto, gated por JWT)
Edge Function identify modificada para usar clave patrocinada dentro del presupuesto, respaldo BYOK en caso contrario
UI + i18n + rutas (página de patrocinio, widget en perfil, EN/ES emparejado)
Pruebas smoke + lint en CI (smoke de Edge Function, gates de tipos/lint)
Rollout + remover ANTHROPIC_API_KEY del servidor (acción del operador)
Pulido M27 — 9 gaps UX (PR #84)
Cobertura completa M27 (PR #94)
Integración Resend SMTP (activa)
Flujo de solicitudes de patrocinio
Página pública /docs/sponsorships
Tour de bienvenida + replay
Selector de modo IA en ObserveView2 (toggle Patrocinado / Llave propia / Local con persistencia en localStorage)

→ Entregado en ObserveView2; cierra #328

Opcional: borrar env ANTHROPIC_API_KEY
admin-console-foundation Consola Admin/Moderador/Experto — PR1 base

24-admin-console.md

7/7

subtareas

Migración: user_roles + admin_audit + has_role + trigger + RLS
Modo chrome console + ConsoleLayout + sidebar + i18n
Edge Function admin + 3 handlers
Tres tabs: Resumen, Expertos (movido), Auditoría
/profile/admin/experts/ → 308 redirect
Spec módulo 24, runbooks, CLAUDE.md
Vitest + Playwright smoke
admin-console-pr2-users-credentials Consola — PR2 (Usuarios + Credenciales, UI de roles)

24-admin-console.md

5/5

subtareas

ConsoleUsersView: búsqueda, chips de rol, slide-over conceder/revocar
ConsoleCredentialsView: conceder rol de investigador con notas
Tabs de experto declarados (5 tabs, todos stub inicialmente)
Pill de rol + enrutamiento de sidebar para moderador + experto
E2E smoke para banners de no-auth en usuarios + credenciales
admin-console-pr3-ops-tabs Consola admin — PR3 (Sync + API + Cron, ops read-only)

24-admin-console.md

5/5

subtareas

ConsoleSyncFailuresView: tabla de grupos de error con filtro de 7 días
ConsoleApiQuotasView: consumo de cuota diaria + gráfico 30 días
ConsoleCronRunsView: tabla de estado de jobs pg_cron
Base de consola moderador: ConsoleModOverviewView + 4 tabs
E2E smoke para banners de no-auth en sync + api + cron
admin-console-pr4-observations Consola admin — PR4 (Observaciones + 4 handlers)

24-admin-console.md

5/5

subtareas

ConsoleObservationsView: tabla global de obs con chips ocultar/obscurecer/licencia
Handler: observation.hide + observation.unhide
Handler: observation.obscure + observation.license_override
RLS obs_public_read excluye observaciones ocultas
E2E smoke para banner de no-auth en observaciones
admin-console-pr5-moderator Consola — PR5 (Consola de moderador: Cola, Comentarios, Suspensiones + 9 handlers)

24-admin-console.md

7/7

subtareas

ConsoleFlagQueueView: triage/resolver/desestimar reportes con slide-over
ConsoleCommentsView: ocultar/mostrar/bloquear/desbloquear comentarios
ConsoleBansView: emitir + levantar suspensiones
Handlers: report.triage/resolve/dismiss (3)
Handlers: comment.hide/unhide/lock/unlock (4)
Handlers: user.ban + user.unban (2)
E2E smoke para banners de no-auth en cola + comentarios + suspensiones
admin-console-pr6-expert Consola — PR6 (Consola de experto: Resumen, Cola de validación, Tu experiencia)

24-admin-console.md

7/7

subtareas

ConsoleExpertOverviewView: contadores KPI + items de acción + contribuciones recientes
ConsoleExpertValidationView: tabla de cola filtrada a expert_taxa + toggle todos los taxa
ConsoleExpertExpertiseView: puntajes por taxón + búsqueda/filtro + gráfico SVG por reino
Stub: ConsoleExpertOverridesView + ConsoleExpertTaxonNotesView
8 nuevas páginas de ruta EN/ES + dispatch de experto en raíz /consola/
Eliminar flag stub de 3 tabs funcionales de experto en console-tabs.ts
E2E smoke para banners de no-auth en validación + experiencia
admin-overview-real-kpis Resumen admin — KPIs reales del estado de la plataforma + alertas + actividad reciente

24-admin-console.md

3/5

subtareas

Tarjetas KPI reales (usuarios, obs, fallos de sync, salud de cron)
Panel de alertas (tasa elevada de fallos, cron desactualizado)
Feed de actividad reciente desde admin_audit
Sparklines de tendencia de 7 dias en cada tarjeta KPI
Exportar snapshot de KPIs como CSV para reportes mensuales
admin-console-pr7-remaining-admin Consola admin PR7 — Badges + Taxa + Karma + Funcionalidades + Bioblitz

24-admin-console.md

6/6

subtareas

ConsoleBadgesView: lista + detalle + gráfico de tiers (botones stub)
ConsoleTaxaView: lista + detalle + mapa de rareza (botones stub)
ConsoleKarmaView: tabla de config + multiplicadores + eventos recientes
ConsoleFeatureFlagsView: grilla de flags por categoría (toggles stub)
ConsoleBioblitzView: stub con aviso de funcionalidad próxima
Fix de RLS admin en karma_events (PR #88)
admin-console-pr8-hardening Consola admin PR8 — CORS estricto + rate limit + pgTAP RLS + 5 handlers + config en DB

24-admin-console.md

18/18

subtareas

CORS: restringir Access-Control-Allow-Origin a rastrum.org + puertos dev/preview localhost
Rate limiter token-bucket (30 req/min/actor, costo 3 para escrituras) — en memoria por isolate
tests/pgtap/rls.sql — 24 aserciones TAP cubriendo has_role + 6 políticas RLS de tabla
db-validate.yml: instalar pgTAP + paso de ejecución de suite RLS
Handler badge.award_manual — upsert user_badges + fila de auditoría
Handler badge.revoke — borrar user_badges + fila de auditoría
Handler taxon.recompute_rarity — llamar RPC refresh_taxon_rarity()
Handler taxon.toggle_conservation — actualizar nom059_status/cites_appendix/iucn_category con audit op taxon_conservation_set
Handler feature_flag.toggle — actualizar app_feature_flags.value + updated_by
Tabla app_feature_flags + políticas RLS + seed desde FEATURE_FLAGS
Tablas karma_config + karma_rarity_multipliers + políticas RLS + seed desde módulos TS
ConsoleFeatureFlagsView: lecturas desde DB + toggle en vivo usando adminClient.featureFlag.toggle
ConsoleBadgesView: botones Award + Revoke activos con formularios deslizantes
ConsoleTaxaView: botones Recompute Rarity + Toggle Conservation activos
ConsoleKarmaView: lecturas desde DB (karma_config + karma_rarity_multipliers)
SDK adminClient: badge.{awardManual,revoke} + taxon.{recomputeRarity,toggleConservation} + featureFlag.toggle
4 nuevas pruebas unitarias para métodos SDK de badge + featureFlag
Sincronización de docs: progress.json + tasks.json + módulo 24 + admin-ops.md + CLAUDE.md
admin-console-pr9-quick-actions Consola admin PR9 — UX admin/moderador (plantillas de motivo + filtros URL + atajos g- + bottom-sheet móvil)

24-admin-console.md

5/5

subtareas

ConsoleSlideOver: pills de motivos pre-traducidos via data attributes
console-filter-state.ts: helpers read/write/applyFilterState (URLSearchParams + history.replaceState)
console-keybindings.ts: chord Vim con prefijo g (g a / g u / g r…) + overlay ? + cierre con Escape
ConsoleSlideOver: bottom-sheet adherente en móvil (env(safe-area-inset-bottom))
Paridad i18n para todas las plantillas de motivo (EN/ES)
admin-console-pr10-subject-ux Consola admin PR10 — UX del afectado (banner de baneo + apelaciones + bloqueo de comentarios + insignia)

24-admin-console.md

6/6

subtareas

BanBanner.astro montado en BaseLayout para baneos activos
tabla ban_appeals + RLS + handlers appeal.accept/reject
AppealView.astro + páginas /profile/appeal/ (EN/ES)
Cola de apelaciones del moderador en ConsoleAppealsView + UI del ciclo de vida
Comments.astro: banner de hilo bloqueado + modo de solo lectura
MyObservationsView: insignia para el dueño de obs ocultas
admin-console-pr11-engineering Consola admin PR11 — higiene de ingeniería (RPC de rate-limit en Postgres + pulido de flash + test del registro)

24-admin-console.md

4/4

subtareas

Tabla rate_limit_buckets + RPC consume_rate_limit_token() SECURITY DEFINER
_shared/rate-limit.ts: reemplazar Map en memoria con llamada RPC async (fail-open)
Pulido de flash en /console/*: div oculto + fallback de 1500ms (modo static-only)
tests/unit/admin-handlers-registry.test.ts: verificación basada en regex
admin-console-pr12-observability Consola admin PR12 — observabilidad (detección de anomalías + resumen semanal + CSV forense + sink de errores)

24-admin-console.md

7/7

subtareas

Tablas admin_anomalies + admin_health_digests + function_errors + RLS
detect_admin_anomalies() — reglas high_rate / bulk_delete / off_hours + cron horario
compute_admin_health_digest() semanalmente lunes 09:00 UTC
Handlers anomaly.acknowledge + audit.export de Edge Function
Helper puro _shared/csv.ts + 17 pruebas unitarias para casos de escape CSV
_shared/error-reporter.ts conectado al catch del dispatcher admin
Tabs admin ConsoleAnomaliesView + ConsoleForensicsView (EN/ES)
admin-console-pr13-future-proofing Consola admin PR13 — future-proofing (roles con expiración + regla de dos personas + webhooks + puntaje de confianza)

24-admin-console.md

9/9

subtareas

user_roles.expires_at + cron diario auto_revoke_expired_roles() 08:15 UTC
Máquina de estados admin_action_proposals + cron horario expire_stale_proposals()
Handlers proposal.create / approve / reject + registro IRREVERSIBLE_OPS
Guardia anti auto-aprobación en _shared/proposal-guards.ts (testeable)
Tablas admin_webhooks + admin_webhook_deliveries + trigger dispatch_admin_webhooks()
Helper de firma HMAC-SHA256 + handlers webhook.create/update/delete/test
Placeholder v1 compute_moderator_trust_score() + vista moderator_trust_scores
Tabs admin ConsoleProposalsView + ConsoleWebhooksView (EN/ES)
4 nuevos runbooks: admin-time-bounded-roles, admin-two-person-rule, admin-webhooks, admin-trust-scores
admin-console-pr14-deferred-cleanup Consola admin PR14 — cierre de pendientes v1.1 (zona por admin + replay de webhooks + cron reconcile + fórmula real de confianza + gate de cumplimiento + dry-run durable de observabilidad)

24-admin-console.md

9/9

subtareas

Columna users.timezone + regla off_hours por admin (LEFT JOIN + COALESCE → UTC)
Selector de zona en Profile → Edit (9 zonas IANA, America/Argentina/Buenos_Aires canónica)
Columnas admin_webhook_deliveries.nonce + .request_id + envelope _meta firmado
reconcile_webhook_deliveries() SECURITY DEFINER + cron 2-min (escribe estado async pg_net)
Fórmula v1.1 de compute_moderator_trust_score() (anomalía + overturn + active_days + recencia, 0-100)
Función pura checkIrreversibleEnforcement() + feature flag enforce_two_person_irreversible (off por defecto)
Prop irreversible en ConsoleSlideOver + toggle 'Requerir aprobación' en slide-overs de Usuarios + Observaciones
.github/workflows/admin-observability-dryrun.yml — one-shot 2026-05-06 + lunes semanal + badge rojo si reconcile atascado
Sincronización de docs: 4 runbooks actualizados, status line de CLAUDE.md actualizado, +4 asserts en sentinel
admin-console-pr15-observability-ui Consola admin PR15 — UI de observabilidad (tarjetas + sparklines del resumen de salud + navegador de errores con ack en lote + drilldown de entregas por webhook + reenvío)

24-admin-console.md

10/10

subtareas

Columnas function_errors.acknowledged_at + acknowledged_by + ack_notes + índice parcial sobre no reconocidos
Enum audit_op +4: health_recompute, error_acknowledge, error_acknowledge_bulk, webhook_replay
Handlers de Edge Function: health.recompute + error.acknowledge[_bulk] + webhook.replay_delivery
ConsoleHealthView — tarjeta hero + sparklines de 12 semanas + recálculo manual (EN/ES)
ConsoleErrorsView — navegador function_errors con filtros URL + pills de severidad + auto-refresco + ack simple y en lote (EN/ES)
ConsoleWebhooksView — drilldown de entregas por webhook + chips de filtro + reenviar última + copiar nonce
Helpers puros: src/lib/health-delta.ts + src/lib/error-severity.ts (10 + 8 aserciones unitarias)
Atajos de consola: g h → salud, g e → errores (g x reasignado a expertos), overlay de ayuda actualizado
32ª aserción de rls.sql + sentinel +3 columnas; test de registro de handlers admin sube a 36 acciones
Runbooks: admin-function-errors.md (nuevo) + sección UI de admin-health-digest + sección PR15 de admin-webhooks + 4 entradas nuevas en admin-ops
admin-console-pr16-entity-browsers Consola admin PR16 — navegadores de entidades de solo lectura (Identificaciones, Notificaciones, Medios, Seguimientos, Listas de vigilancia, Proyectos, Cambios de taxón) sobre plantilla compartida + runtime

24-admin-console.md

13/13

subtareas

Índices hot-path (filter_field, created_at DESC) en identifications, notifications, media_files, follows, projects, watchlists + políticas admin-SELECT en notifications + watchlists
Plantilla compartida ConsoleEntityBrowser.astro + runtime entity-browser.ts (paginación servidor, filtros via URL, lookups de claves foráneas, dropdowns auto-poblados, expansión de drilldown, rowIdFromRow para tablas con PK compuesta)
ConsoleIdentificationsView — filtra por identificador/taxón/fuente/validado/calidad de investigación; drilldown completo + raw_response + deep-link a la observación
ConsoleNotificationsView — filtra por destinatario/tipo (auto-poblado)/estado de lectura/fecha desde; drilldown completo del payload jsonb
ConsoleMediaView — filtra por tipo/estado/observación; miniatura con loading=lazy; drilldown completo + URL R2 + EXIF
ConsoleFollowsView — filtra por seguidor/seguido (autocompletado)/estado/nivel; rowIdFromRow sintetiza id estable desde (follower_id, followee_id)
ConsoleWatchlistsView — filtra por usuario/taxón/nombre científico (ilike); drilldown completo. Nota de esquema: no hay columna `region` en v1; usa radius_km + digest_only como facetas visibles
ConsoleProjectsView — respaldado por la vista projects_with_geojson (security_invoker); filtra por propietario/visibilidad/nombre; drilldown GeoJSON + species_list + conteo lazy de observaciones via head:true count(*)
ConsoleTaxonChangesView — respaldado por admin_audit filtrado a operaciones taxon_* mediante sentinel de filtro base; filtra por taxón/acción/actor/fecha; drilldown diff before/after en JSON
Wrappers de página EN+ES para los 7 navegadores (14 páginas); console-tabs.ts registra taxon-changes; routes.consoleTaxonChanges + par de etiquetas en routeTree
i18n: namespaces console.{identifications,notifications,media,follows,watchlists,projects,taxonChanges}View EN+ES + etiquetas de tabs + cadenas compartidas de entityBrowser
Pruebas: console-tabs.test.ts actualizado a 39 tabs (32 + 7 PR16); entity-browser.test.ts (212 LOC: predicados de filtros + applyFilters + helpers de render); 681 pruebas pasan
Runbook: docs/runbooks/admin-entity-browsers.md (patrón de plantilla, características de rendimiento, receta para añadir un nuevo navegador, manejo de PK compuestas, facetas lazy)
community-observers-pr17-ux-fixes Fixes UX en observadores comunidad — explicador de privacidad + CTA país + Cercanos con GPS

28-community-discovery.md

10/10

subtareas

Banner explicativo de privacidad por defecto — copy según el viewer (privado vs público); aparece con 0 filas + sesión iniciada
CTA inline para fijar país — sólo si viewer en sesión y country_code IS NULL; enlaza a /profile/edit/
RPC community_observers_nearby_at(lat, lng, radius_m, …) SECURITY INVOKER; sólo a authenticated; reutiliza índice GiST en centroid_geog
📍 Botón Usar mi ubicación con navigator.geolocation; coords en sessionStorage (clave rastrum.community.gps), NUNCA en la URL — prevención de filtración por Referer
Fallback a Cercanos por centroide al denegar geolocalización; toast amigable
Helpers loadGps / saveGps / clearGps en community-url.ts (testeable, inyección de dependencias)
Test de regresión: 'NUNCA serializa coords GPS en la URL' + 9 casos de round-trip / malformado / fuera de rango
loadViewerCommunityMeta() — una sola consulta para {signedIn, profilePublic, countryCode}; sin N+1
Entradas en sentinel verify para los dos RPCs de Cercanos
Regla 4 del M28 en CLAUDE.md + sección de aclaraciones UX en docs/runbooks/community-discovery.md
console-chrome-rendering-fix Fix del chrome de la consola — restaurar sidebar + role pills (70 páginas usaban BaseLayout, ConsoleLayout era código muerto)

24-admin-console.md

10/10

subtareas

Diagnóstico: ConsoleLayout existía pero sin uso; las 70 páginas /console + /consola importaban BaseLayout dejando el chrome sin renderizar
Refactor de ConsoleLayout para SSG: props (allRoles, activeRole, currentPath, identity) ahora opcionales con defaults sensatos
Renderiza en server las 3 listas de tabs por rol con data-role, todas ocultas; script cliente las revela según getUserRoles()
Mismo patrón para los role pills: las 3 ocultas, JS revela las del viewer; rol activo con acento de su rol
Resaltado del tab activo via currentPath = Astro.url.pathname (funciona en build estático porque cada ruta tiene su propio HTML)
Reemplazar BaseLayout → ConsoleLayout en las 35 páginas EN console + 35 ES consola (70 total)
BaseLayout: console.warn en build si una ruta /console/* o /consola/* pasa por BaseLayout (guard de regresión)
Verificación: grep en dist/ confirma <aside + data-console-pill + data-role admin/moderator/expert en todas las páginas muestreadas
Nuevo runbook: docs/runbooks/admin-chrome-rendering.md (la invariante 'sólo ConsoleLayout')
Sección Console de CLAUDE.md: 4ª regla load-bearing documentando el contrato 'sólo ConsoleLayout' + cross-link al runbook de chrome rendering
ux-confidence-ring Anillo de confianza (arco SVG en emerald→ámbar→rojo) en lugar de pastilla con porcentaje

13-identifier-registry.md

6/6

subtareas

Anillo de confianza (arco SVG en emerald→ámbar→rojo) en lugar de pastilla con porcentaje — más fácil de leer para no-técnicos
Componente SVG arc con paradas de color graduadas
Relleno animado del arco al montar para engagement visual
aria-label accesible con confianza numerica para lectores de pantalla
Reemplazar pill de porcentaje en vistas de detalle y tarjeta compartida
Test snapshot Vitest para renderizado en valores limite
ux-quick-taxon-chips Íconos rápidos de taxón bajo el subidor de fotos (🌿 planta · 🐦 ave · 🐾 mamífero · 🐛 insect

02-observation-form.md

6/6

subtareas

Íconos rápidos de taxón bajo el subidor de fotos (🌿 planta · 🐦 ave · 🐾 mamífero · 🐛 insecto · 🍄 hongo) — orienta la cascada y fija expectativas honestas
Componente fila de chips con 5 iconos de reino debajo del area de foto
Tocar chip establece campo taxon_hint en observacion para priming de cascada
Feedback visual: chip seleccionado con anillo + deseleccionar al segundo tap
Labels i18n para tooltips de los 5 chips en EN + ES
Targets tactiles movil (min 44px) con scroll horizontal en pantallas angostas
ux-photo-compression Compresión automática de fotos > 4 MP a 4 MP vía canvas antes de subir

02-observation-form.md

6/6

subtareas

Compresión automática de fotos > 4 MP a 4 MP vía canvas antes de subir — reduce R2 4×, acelera la sincronización en redes lentas, sin pérdida de calidad de ID
Resize basado en canvas a 4 MP max antes de subir (preserva proporcion)
Parametro de calidad JPEG ajustado para balance precision ID vs tamano
Omitir compresion para imagenes ya bajo umbral de 4 MP
Orientacion EXIF preservada a traves del pipeline de compresion
Reduccion de tamano registrada para analiticas de rendimiento de sync
ux-share-button Botón de compartir en la tarjeta de resultado → /share/obs/<id> con la OG card existente;

03-observation-detail.md

6/6

subtareas

Botón de compartir en la tarjeta de resultado → /share/obs/<id> con la OG card existente; clave para difusión viral durante el lanzamiento familiar
Integracion Web Share API con fallback a copiar al portapapeles
URL de compartir apunta a /share/obs/<id> con tarjeta OG pre-renderizada
Boton compartir posicionado en tarjeta de resultado tras identificacion
Toast de confirmacion tras compartir exitosamente o copiar al portapapeles
Tracking de eventos para taps en boton compartir
ux-save-as-draft Guardar observación como borrador sin GPS

02-observation-form.md

6/6

subtareas

Guardar observación como borrador sin GPS — el formulario actual bloquea el submit si falta ubicación, lo que rompe el flujo en zonas sin señal
Estado borrador en entradas del outbox Dexie cuando falta GPS
Flujo de retomar borrador: listar borradores con acciones editar/eliminar
Indicador visual distinguiendo borradores de observaciones pendientes de sync
Auto-guardado a borrador al navegar fuera del formulario incompleto
Badge de conteo de borradores en item de navegacion observar
ux-onboarding-tour Onboarding al primer registro
6/6

subtareas

Onboarding al primer registro — 3 tarjetas descartables (instala la PWA → toma una foto → mira tu perfil); saltable; sin repeticiones
3 tarjetas de onboarding descartables (instalar PWA, tomar foto, ver perfil)
Tarjetas mostradas solo en primer registro via flag localStorage
Boton saltar para descartar todas las tarjetas restantes
Nunca se repite tras descarte (persistencia localStorage)
Contenido de tarjetas bilingue EN + ES segun locale del usuario
ux-chat-suggestions Chips de sugerencia de seguimiento bajo cada respuesta de IA en chat (¿Es venenosa?, ¿Cómo

13-identifier-registry.md

6/6

subtareas

Chips de sugerencia de seguimiento bajo cada respuesta de IA en chat (¿Es venenosa?, ¿Cómo distinguir de X?, Hábitat) — reduce fricción de arranque
Fila de chips de sugerencia renderizada debajo de cada respuesta AI en chat
Chips contextuales basados en especie identificada (toxicidad, habitat, similares)
Tocar chip auto-rellena input de chat y envia pregunta de seguimiento
Labels de chips bilingues en EN + ES
Chips ocultos tras seleccionar uno para evitar desorden
ux-chat-persistence Persistir historial de chat por dispositivo en Dexie
6/6

subtareas

Persistir historial de chat por dispositivo en Dexie — actualmente se pierde al recargar
Tabla Dexie para historial de conversacion de chat (por dispositivo, por usuario)
Restaurar conversacion previa al montar pagina /chat
Boton limpiar conversacion con dialogo de confirmacion
Conversacion auto-podada tras 50 mensajes para limitar tamano IndexedDB
Scroll al fondo al cargar pagina cuando se restaura conversacion
ux-explore-time-slider Deslizador temporal en el mapa /explore

12-explore-map.md

6/6

subtareas

Deslizador temporal en el mapa /explore — arrastra meses para ver patrones fenológicos; impacta visualmente a nuevos visitantes
Componente slider de rango para seleccion de mes en mapa /explore
Fuente GeoJSON filtrada por rango de fecha al cambiar slider
Transicion animada al filtrar observaciones por ventana de tiempo
Etiquetas de mes mostradas debajo del slider con formato por locale
Boton reset para limpiar filtro de tiempo y mostrar todas las observaciones
ux-skeleton-screens Reemplazar spinners desnudos con esqueletos en /observations, /explore y perfil
6/6

subtareas

Reemplazar spinners desnudos con esqueletos en /observations, /explore y perfil — la plataforma se siente más ágil en su conjunto
Componentes skeleton para tarjetas de observacion, secciones de perfil, grilla explore
Animacion shimmer CSS en elementos skeleton para percepcion de carga
Dimensiones skeleton coinciden con contenido real para prevenir layout shift
Transicion suave de skeleton a contenido real al cargar datos
Colores skeleton compatibles con modo oscuro via propiedades CSS custom
ux-voice-chat-input Entrada de voz vía SpeechRecognition en /chat

13-identifier-registry.md

6/6

subtareas

Entrada de voz vía SpeechRecognition en /chat — accesibilidad, sobre todo para hablantes nativos de lenguas indígenas
Integracion API SpeechRecognition con manejo de permisos de microfono
Boton de entrada de voz en /chat con animacion de indicador de grabacion
Transcripcion auto-rellena campo de input de chat al terminar habla
Deteccion de idioma segun locale actual (EN o ES) para reconocimiento
Mensaje de fallback cuando SpeechRecognition no es soportado
ux-first-observation-celebration Confetti + banner 'Bienvenido a Rastrum 🌱' en la primera observación sincronizada del usua

08-profile-activity-gamification.md

6/6

subtareas

Confetti + banner 'Bienvenido a Rastrum 🌱' en la primera observación sincronizada del usuario
Animacion de confeti al completar primera observacion sincronizada
Banner de bienvenida bilingue con ilustracion de especie
Flag de primera observacion almacenado en perfil para prevenir repeticion
Banner se descarta automaticamente tras 5 segundos con boton de cierre
Evento de feed de actividad registrado para hito de primera observacion
ux-indigenous-taxa-search Búsqueda de taxón en lenguas indígenas (Zapoteco / Náhuatl / Maya / Mixteco / Tseltal → no

07-licensing.md

0/6

subtareas

Búsqueda de taxón en lenguas indígenas (Zapoteco / Náhuatl / Maya / Mixteco / Tseltal → nombre científico); requiere corpus + gobernanza per local-contexts
Alianza de corpus con CONABIO y Co-PIs comunitarios para datos de nombres indigenas
Tabla de busqueda de nombres indigenas a nombre cientifico
Autocompletado de busqueda soportando entrada en lengua indigena
Revision de gobernanza y protocolo de consentimiento comunitario
Integracion de avisos BC/TK de Local Contexts para taxa culturalmente sensibles
ux-photo-dedupe Deduplicación de imágenes al enviar

02-observation-form.md

6/6

subtareas

Deduplicación de imágenes al enviar — hash perceptual avisa cuando se re-sube la misma foto
Computacion de hash perceptual (pHash) al subir imagen en formulario
Advertencia de duplicado cuando hash coincide con subida existente en sesion
Usuario puede ignorar advertencia y enviar duplicado intencionalmente
Comparacion de hash corre en cliente sin round-trip al servidor
Presupuesto de rendimiento: hash bajo 100ms por imagen en movil gama media
ux-streak-push Notificación push a las 20:00 hora local cuando una racha está a 1 día de romperse

08-profile-activity-gamification.md

6/6

subtareas

Notificación push a las 20:00 hora local cuando una racha está a 1 día de romperse — opcional, una notificación nocturna
UI de opt-in para suscripcion Web Push en preferencias de notificacion del perfil
Edge Function para verificar rachas en riesgo (1 dia de romperse) cada noche
Payload de notificacion push con conteo de racha y mensaje motivacional
Respetar zona horaria del usuario para entrega a las 8 PM local
Limite de tasa: maximo una notificacion push por usuario por dia
delete-observation-atomic Función Edge atómica delete-observation

03-observation-detail.md

6/6

subtareas

Función Edge atómica delete-observation — borra fotos en R2 + tarjeta OG además de filas DB; sin blobs huérfanos
Edge Function que elimina observacion + media_files + blobs R2 en una transaccion
Eliminacion de objetos R2 via DELETE presignado o service binding
Limpieza de tarjeta OG junto con eliminacion de media (sin tarjetas huerfanas)
Verificacion RLS asegurando que solo dueno o admin puede eliminar
Dialogo de confirmacion en UI antes de eliminar irreversiblemente
suggest-from-share-page Sugerir identificación desde cualquier página /share/obs

22-community-validation.md

6/6

subtareas

Sugerir identificación desde cualquier página /share/obs — lista de sugerencias comunitarias visible para todos
Modal de sugerir identificacion en pagina /share/obs para input comunitario
Lista de sugerencias comunitarias visible a todos debajo de ID primaria
Verificacion auth: solo usuarios autenticados pueden enviar sugerencias
Sugerencia incluye nombre de taxon, confianza y notas opcionales
Recomputacion de consenso disparada al agregar nueva sugerencia
karma-phase-2-engagement Capas de engagement

23-karma.md

0/6

subtareas

Capas de engagement — notificaciones, resumen, leaderboards (Fase 2)
Notificacion toast en hitos de karma (100, 500, 1000 puntos)
Email semanal de resumen de karma con puntos ganados y cambio de rango
Pagina de tabla de lideres en /explore/leaderboard con visibilidad opt-in
Tooltip de desglose de karma en perfil mostrando puntos por categoria
Desafios de karma estacionales con multiplicadores de bonificacion temporales
karma-phase-3-conservation-bonuses Karma

23-karma.md

0/6

subtareas

Karma — bonos de conservación IUCN/NOM-059 (Fase 3)
Multiplicador por estado Lista Roja IUCN (Vulnerable 1.5x, En Peligro 2x, CR 3x)
Multiplicador por categoria NOM-059 para especies amenazadas endemicas mexicanas
Bonificacion por primera observacion de especie para nuevos registros en una region
Bonificacion por observacion en area protegida cuando GPS cae en poligono ANP
Tabla de lideres de karma de conservacion filtrada por region y periodo

v1.2 Privacidad del perfil y perfil público

profile-privacy-matrix Matriz de privacidad del perfil (módulo 24)

25-profile-privacy

15/15

subtareas

Esquema: users.profile_privacy JSONB (matriz de 19 claves) + dismissed_privacy_intro_at + índice GIN
Función SQL STABLE can_see_facet(target, facet, viewer)
RPC en lote can_see_facets() (resultado jsonb)
Backfill desde el booleano profile_public del módulo 08
Política RLS users_update_self_privacy
Añadir Privacidad como 5ª pestaña en SettingsShell.astro (/profile/settings/privacy/) con 3 presets + matriz de 19 filas + vista previa en vivo
Ampliar union activeTab de SettingsShell con 'privacy' + añadir clave i18n settings.tabs.privacy (EN+ES)
Añadir 4 entradas al índice de la paleta de comandos PR-4: Configuración de privacidad, Pokédex, Ver mi perfil público, Hacer perfil privado (acción)
Registrar entradas routeTree: profileSettingsPrivacy, publicProfile, publicProfileDex (PR-3 breadcrumbs/footer)
Insertar 6º paso de onboarding (PR-5 spotlight tour): elige tu preset de privacidad
Coordinación con módulo 23: reemplazar user_expertise_public_read por user_expertise_facet_read
Paso de onboarding (módulo 18): elige tu preset de privacidad
Gateo de enlace de votante en módulo 22 (experto anónimo cuando profile=private)
Banner introductorio + dismissed_privacy_intro_at
Test pgTAP: cobertura de 9 celdas de can_see_facet()
public-profile-route Perfil público en /u/<username>/

25-profile-privacy

14/14

subtareas

Rutas Astro: /en/u/[username]/index.astro + /es/u/[username]/index.astro
Orquestador PublicProfileView con llamada en lote a can_see_facets()
Componentes ProfileHero + ProfileObservationMap + ProfileTopSpecies + ProfileValidationReputation
ProfileEmptyState (equivalente a 404 cuando profile=private)
Gateo de OG card (renders público/registrado/privado)
Filtro de sitemap: excluir profile_privacy.profile <> 'public'
Meta noindex + nofollow en perfiles no públicos
Entidad JSON-LD Person en /u/<username>/ cuando profile=public (barra SEO PR-C/E)
Metadatos por página para /u/<username>/: canonical, par hreflang en↔es, OG/Twitter (paridad PR-B)
Enumeración en sitemap de perfiles públicos (filtrar profile_privacy.profile = 'public', emitir pares hreflang)
Redirección 301 desde la ruta heredada /{lang}/profile/u/?username=… hacia /u/<username>/
Actualizar enlaces internos: MyObservationsView, ProfileView, ExploreRecentView, ValidationQueueView, /share/obs
Reescribir PublicProfileView para usar can_see_facets() en vez de profile_public
Quitar checkbox profile_public de ProfileEditForm; StreakCard ajusta profile_privacy.profile
profile-widgets-richer Widgets de perfil enriquecidos (heatmap, dona, racha, especies top, mini-mapa)

25-profile-privacy · 08-profile-activity-gamification

5/5

subtareas

ProfileCalendarHeatmap (cuadrícula SVG de 12 meses, click → lista filtrada)
ProfileTaxonomicDonut (desglose reino/filo/familia)
ProfileStreakRing (arco actual + arco exterior atenuado del más largo)
Vistas gateadas por faceta: profile_calendar_buckets, profile_taxonomic_donut, profile_top_species, profile_karma, profile_pokedex
ProfilePokedexLink + ruta de visitante /u/<username>/dex/
social-graph-m26 Grafo social + reacciones (Módulo 26)

26-social-graph.md

13/13

subtareas

tabla follows + trigger de contadores + helpers de privacidad (social_visible_to, is_collaborator_of) + desbloqueo de precisión de coordenadas para colaboradores
tablas de reacciones por objetivo: observation_reactions, photo_reactions, identification_reactions (+ RLS, enums de kind, restricciones de unicidad)
bloqueos (lectura simétrica) + reportes (cola con enum de razón) + notificaciones (enum de kind + cron de poda a 90 días)
triggers de propagación: follow → notificación, observation_reactions → notificación (omite destinatarios bloqueados)
Edge Functions: follow (solicitar/aceptar/rechazar + límite de tasa), react (toggle idempotente), report (con email al operador)
Tipos TypeScript (types.social.ts) + cliente social (followUser, react, reportTarget, blockUser) + 5 pruebas unitarias
i18n: namespace socialgraph.* EN+ES (evita el namespace plano existente social.*) + nuevas rutas (inbox, profileFollowers, profileFollowing)
UI: ReactionStrip, FollowersView, FollowingView, InboxView, BlockedUsersList + páginas EN/ES emparejadas
pruebas: regresión tests/sql/social-rls.sql + smoke tests/e2e/social.spec.ts
esquema aplicado en producción (auto-disparado por db-apply.yml al mergear #43); bug MIN(uuid) corregido en #62
Edge Functions desplegadas (follow/react/report) — auto-desplegadas por deploy-functions.yml tras la revamp CI/CD en #62
Integración UI (#63 + #64): BellIcon→/inbox + badge no leídos, tarjetas ricas (avatares + thumbs + iconos por tipo + skeletons + vacío), pills de seguidores/siguiendo en perfil, menú ⋮ en perfil (Bloquear + Reportar), ReportDialog compartido en BaseLayout (focus trap + Esc + click fuera), FollowButton usa Edge Function con estado Solicitado, ReactionStrip activo en /share/obs/
Seguimientos v1.1 (ENTREGADOS en PR #97 + #100 + #101): ver social-graph-m26-v11, visitor-pokedex-route, deploy-functions-resilience abajo
social-graph-m26-v11 Pulido M26 v1.1 — reacciones en feed + Bloquear/Reportar en tarjetas/comentarios + ARIA

26-social-graph.md

6/6

subtareas

Vista observation_reaction_summary (security_invoker = true) para conteos por batch sin N+1
Chip de conteo ❤ N en tarjetas ExploreRecentView + MyObservationsView
Menú ⋮ en tarjetas de observación (Bloquear + Reportar) — mismo patrón que PublicProfileViewV2; oculto en tarjetas propias
Bloquear/Reportar por comentario en Comments.astro (lazy import de blockUser, ReportDialog con target='comment')
ARIA: región aria-live en toggle de ReactionStrip, role=menu/menuitem en overflows, aria-label por fila en InboxView
i18n EN/ES bajo socialgraph.cards.* + socialgraph.reactions.* (12 keys nuevas)
visitor-pokedex-route Ruta pública de Pokédex visitante /u/dex/?username=

25-profile-privacy.md

6/6

subtareas

Parametrizar PokedexView.astro con prop visitor (fallback a vista del dueño cuando ausente)
Gate de privacidad vía RPC can_see_facet(target, 'pokedex', viewer); fallback privado cuando false
Páginas EN + ES emparejadas en /u/dex/ con noindex,nofollow
PublicProfileViewV2.astro — re-mostrar link Pokédex para visitantes (dueño → /profile/dex/, visitante → /u/dex/?username=)
OG card: slug profile-dex-visitor en generate-og.ts + ogSlugForPath() en BaseLayout (EN + ES)
i18n: keys pokedex.visitor_* (title, subtitle, empty, private, not_found, not_found_cta, back_to_profile)
deploy-functions-resilience Fijar imports esm.sh en Edge Functions para resiliencia de deploy

13-identifier-registry.md

3/4

subtareas

Fijar @supabase/supabase-js@2 → @2.39.7 en 24 archivos Edge Function
Verificar que otros imports esm.sh ya estaban fijados (aws-sdk@3.658.1, jszip@3.10.1, zod@3.23.8)
Pin inline durante cascada de merge para recompute-user-stats/index.ts que llegó a mid-PR
Seguimiento: fijar imports en los 5 nuevos handlers admin de PR #99 cuando #99 mergee (~5 LOC patch)
m26-v1-1-review-followups Seguimientos de review M26 v1.1 — pulido menor de los notes en PR #100/#101

26-social-graph.md

6/6

subtareas

Alias ?u= en readUsername() — eliminado (audit no encontró referencias fuera de docs en PublicProfileViewV2, FollowingView, FollowersView, PokedexView)
document.title en JS → usar key i18n por consistencia (PublicProfileViewV2, ExploreSpeciesView, PokedexView)
runVisitor catch-all → distinguir estado error vs not-found (modo visitante Pokedex ahora muestra visitor_load_error para errores de red/RPC, visitor_not_found solo para PGRST116 0-rows)
Componente ConfirmDialog — reemplaza confirm()/alert() en flujo Block + delete-photo (patrón ReportDialog; cierra notas de reviews PR #101 + #125)
Extraer lógica de overflow-menu a lib/overflow-menu.ts (reemplaza duplicación 3× en PublicProfileViewV2, ExploreRecentView, Comments — wireOverflowMenu(wrap, trigger, menu) regresa teardown)
Estandarizar naming de data-attrs fave_count_one vs fave_aria_one — ambos consumidores ahora usan data-label-fave-aria-one/other
community-discovery-m28 Descubrimiento comunitario (Módulo 28)

28-community-discovery.md

5/5

subtareas

PR1 — deltas de schema: 6 columnas nuevas en users + 7 índices parciales filtrados a (NOT hide_from_leaderboards AND profile_public) + tabla iso_countries + seed bilingüe de 28 países + normalize_country_code() + vistas duales
PR2 — Edge Function recompute-user-stats + cron nocturno 08:00 UTC + wrapper recompute_user_stats() SECURITY DEFINER
PR3 — backfill de recompute-user-stats tras deploy para sembrar counters/centroides/country_code de usuarios existentes. Automatizado como workflow_dispatch en .github/workflows/community-backfill.yml (POST a la EF, surfacea rows_updated/elapsed_ms en la Actions UI, idempotente)
PR4 — Perfil → Editar: selector de país + toggle 'Mostrarme en descubrimiento comunitario' (UI-true → DB-false hide_from_leaderboards) + country_code_source 'auto'/'user' para badge 'inferido por tu región' + helper puro buildCommunityDiscoveryPayload() + 9 tests unitarios
PR5+PR6 — aterrizaje atómico: página CSR /community/observers/ + chips componibles + sign-in en Nearby + serializador URL + RPC community_observers_nearby + split del MegaMenu + subheading en MobileDrawer + reescritura i18n + OG card + roadmap + módulo → 'shipped'
obs-detail-redesign Rediseño de la página de detalle de observación — viewer + edición de dueño
6/7

subtareas

PR1 — extraer MapPicker.astro reutilizable de ObservationForm. IDs HTML por instancia (sufijo -${pickerId}). A11y del modal nativo preservada. Regresión Playwright para el flujo del observe form
PR2 — schema: observations.last_material_edit_at + media_files.deleted_at + idx_media_files_active + trigger observations_material_edit_check_trg. Extraer observation-enums.ts + namespace i18n obs_detail.* EN+ES
PR3 — PhotoGallery.astro (lightbox nativo + teclado + swipe + probe canShare + aria-labels dinámicos 'Foto N de M') + extracción de ShareObsView.astro a layout dos columnas / apilado móvil + share/obs/index.astro adelgazado + lectura de coordenadas + badge 'Editado tras IDs' + Playwright e2e (5 casos) + strings ternarios de ShareObsView migrados a obs_detail.view.*
PR4 — ObsManagePanel.astro tab Detalles: edición de fecha/hora + hábitat + clima + establishment_means + override científico + notas + obscure-level. Trigger material-edit dispara al guardar
PR5 — ObsManagePanel.astro tab Ubicación: picker view sólo-lectura (pickerId='obs-detail-loc-view') + picker edit-modal (pickerId='obs-detail-edit') conectados por wireManagePanelLocation; helper pointGeographyLiteral + 5 casos vitest + 5 casos Playwright e2e
PR6 — Tab Fotos + Edge Function delete-photo (transacción atómica via RPC delete_photo_atomic SECURITY DEFINER: media_files.deleted_at + degradar identifications limpiando validated_by/validated_at/is_research_grade + observations.last_material_edit_at). Subir fotos via R2 + diálogo confirm cascade-photo dirigido por helper willDemote() (cubierto por vitest)
Seguimientos v1.1: cron gc-orphan-media (GC R2 de blobs soft-deleted); decisión nativo vs PhotoSwipe del lightbox
projects-anp-m29 Proyectos (Módulo 29) — protocolos de polígono ANP

29-projects-anp.md

3/4

subtareas

Schema: projects (slug UNIQUE, polygon geography(MultiPolygon, 4326), visibility público/privado) + project_members + RLS + índice GIST
Trigger BEFORE INSERT/UPDATE OF location: ST_Covers(polygon, NEW.location) → auto-etiqueta NEW.project_id (respeta override manual, desempate ORDER BY created_at ASC)
RPC upsert_project SECURITY DEFINER + vista projects_with_geojson WITH (security_invoker = true) + UI (List/New/Detail) en /{en,es}/{projects,proyectos}/
Seguimiento v1.1: exponer link de Projects desde el chrome (columna Biodiversidad del Explore MegaMenu) — actualmente sólo accesible via URL directa
cli-batch-import-m30 CLI de importación masiva para tarjetas de cámaras trampa (Módulo 30)

30-cli-batch-import.md

4/4

subtareas

Paquete cli/ Node 20+ TypeScript (rastrum-import) — walker + lector EXIF + orquestador
Endpoint Edge Function POST /api/upload-url — emite URLs presignadas R2 PUT con scope por token rst_*
Flag --project-slug auto-etiqueta cada observación en el polígono M29. El archivo de estado vuelve la importación reanudable entre recuperaciones de SD
Construido para el flujo CONANP-Oaxaca / DRFSIPS / PROREST 2026 (500–2000 imágenes por deployment)
camera-stations-m31 Estaciones de cámara + seguimiento de esfuerzo de muestreo (Módulo 31)

31-camera-stations.md

3/4

subtareas

Schema: camera_stations (FK project_id + station_key + coords + camera_model)
Schema: camera_station_active_periods (inicio/fin) — múltiples por estación para redespliegues a lo largo del año
Base para índices de fauna futuros: Índice de Abundancia Relativa (RAI), tasa de detección por 100 noches-trampa, riqueza de especies — todos dependen de saber cuánto tiempo estuvo muestreando la cámara
Seguimiento v1.1: UI para capturar periodos activos (formulario CRUD + selector de estación en observaciones + capa de mapa de estaciones)
multi-provider-vision-m32 Visión multi-proveedor + modelo por patrocinador + pool de plataforma (Módulo 32)

32-multi-provider-vision.md

4/4

subtareas

_shared/vision-provider.ts — interfaz VisionProvider implementada por 6 proveedores (Anthropic api_key + oauth_token, Bedrock con AWS Sig V4, OpenAI, Azure OpenAI, Gemini)
Columna preferred_model por patrocinador en ai_credentials — beneficiarios pueden pedir Haiku/Sonnet/Opus o equivalentes Bedrock (cierra #116)
Proveedores OpenAI / Azure OpenAI / Google Gemini / Vertex AI (cierra #118)
Pool de llamadas a nivel plataforma: tabla sponsor_pools + RPC consume_pool_slot (cierra #115)
social-graph-m26-ui Integración UI de M26

26-social-graph.md

7/7

subtareas

Integración UI de M26 — campana→bandeja + badge no leídos, tarjetas ricas, pills de seguidores/siguiendo + menú ⋮ Bloquear/Reportar, ReportDialog compartido con focus trap, ReactionStrip en /share/obs/, FollowButton sobre la nueva Edge Function (estado Solicitado en perfiles privados)
Icono de campana apuntando a /inbox con badge de no leidos
Tarjetas de notificacion ricas con avatares, miniaturas, iconos por tipo, skeletons
Pills de seguidores/siguiendo en perfil + menu overflow (Bloquear + Reportar)
Modal ReportDialog compartido con focus trap, cierre Esc, radio de razon + nota
FollowButton llamando Edge Function follow m26 con estado Requested para perfiles privados
ReactionStrip reescrito para auto-hidratar e interactivo en /share/obs/
ci-cd-edge-auto-deploy CI/CD
5/6

subtareas

CI/CD — auto-deploy de Edge Functions en push con filtro de ruta (refleja db-apply.yml). Lista derivada del filesystem elimina drift; workflow_dispatch manual preservado para rollback quirúrgico
Workflow GitHub Actions filtrado por path en cambios supabase/functions/**
Lista de funciones derivada del filesystem elimina drift entre enum UI y loop de deploy
workflow_dispatch manual preservado para rollback quirurgico de funciones individuales
Job de deploy usa version fija de CLI supabase para reproducibilidad
Notificacion Slack/email en fallo de deploy con nombre de funcion y log de error
ci-rls-presence-check Gate CI
6/6

subtareas

Gate CI — db-validate.yml falla si cualquier tabla public.* no tiene ENABLE ROW LEVEL SECURITY. Cierra el ciclo del lint rls_disabled_in_public de Supabase atrapando RLS faltante en PR review en lugar de via el email de alerta de seguridad post-merge
Workflow db-validate.yml verificando que todas las tablas public.* tienen RLS habilitado
Query SQL contra pg_catalog para detectar tablas sin RLS
Job CI falla PR si alguna tabla publica carece de RLS (exit code no-cero)
Mensaje de error claro listando tablas especificas sin RLS en output CI
Corre en cada PR que toca paths supabase/ o docs/specs/infra/

v1.5 Capa Territorial Planeado

biodiversity-trails Senderos de Biodiversidad con waypoints GPS + métricas de diversidad
0/5

subtareas

Tabla trails
UI de grabación de sendero
Vinculación observaciones-sendero
Métricas de diversidad por sendero
Página de detalle + render en mapa
pits-qr PITs + anclas QR/NFC
0/5

subtareas

Tabla pits
Página por PIT con observaciones
Generador QR para impresión
Documentación de protocolo NFC
Estadísticas por PIT
spatial-analysis Análisis espacial: capas GeoJSON ANP/INEGI/INAH
0/5

subtareas

GeoJSON ANP (CONANP)
Límites municipales INEGI
Polígonos INAH
Toggle de capas en MapLibre
Consultas PostGIS ST_Within
diversity-indices Índices de diversidad: S, H', D, Chao1, Pielou J
0/4

subtareas

Edge Function diversity-stats
Cálculos matemáticos
Curvas de rarefacción
Render en páginas de sendero / evento
trail-pdf-export Exportación PDF de senderos (estilo guía de campo)
0/10

subtareas

Render PDF server-side
Mapa, lista de especies, stats
Plantillas bilingües
Seleccion de motor PDF (Puppeteer vs react-pdf/pdfkit)
Portada con snapshot del mapa del sendero
Tabla checklist de especies con miniaturas y nombres
Seccion de estadisticas de diversidad (riqueza, Shannon, conteo)
Plantillas PDF bilingues EN/ES con formato de fecha por locale
Endpoint Edge Function para generacion PDF bajo demanda con auth
Boton de descarga en ExportView junto a opciones CSV

v2.0 Institucional Planeado

camera-trap-advanced Cámara trampa: modelado de ocupación, histogramas
0/9

subtareas

Pipeline estadístico R/Python
Histogramas por especie y hora
Análisis de grid multi-cámara
Pipeline de modelado de ocupacion con matrices de deteccion
Histograma de actividad por especie y hora del dia (grafico polar 24h)
Analisis de grilla multi-camara agregando detecciones entre estaciones
Calculo de esfuerzo noches-trampa desde periodos activos (dep. M31)
RAI por especie por estacion con intervalos de confianza
Exportar resultados de ocupacion + actividad como CSV e integracion DwC-A
gbif-publisher Publicador de datasets GBIF + DOI
0/10

subtareas

Exportación DwC-A continua
Releases versionados con DOIs
Generador de citas
Generacion automatica de ZIP DwC-A via Edge Function programada
Archivo EML de metadatos del dataset con contacto, cita y licencia
Releases versionados del dataset con versionado semantico y changelog
Acunacion de DOI via integracion con API de Zenodo o DataCite
Generador de citas en formatos BibTeX + APA + RIS
Registro en GBIF IPT y configuracion de endpoint
Runbook de operador para configuracion inicial de cuenta GBIF
regional-ml Pipeline de entrenamiento ML regional

07-licensing.md

0/4

subtareas

Fine-tuning continuo
Aprendizaje federado
Model cards por release
Compuerta de licencia
b2g-dashboard Dashboard B2G SaaS para CONANP / agencias
0/5

subtareas

App separada en b2g.rastrum.app
Plantillas por agencia
Suscripciones Stripe
Logs de auditoría + SLA
!
Licencia comercial BirdNET firmada

Bloqueado por: Proceso con Cornell

inat-bridge Puente iNaturalist import/export
0/4

subtareas

Flujo OAuth con iNat
Importar observaciones de iNat
Exportar datos verificados
Compatibilidad con rate limits

v2.5 IA + RA Planeado

scout-full Rastrum Scout — IA conversacional de campo completa
0/4

subtareas

Refinamiento multi-turno
Retrieval por región
Render de citas con fuentes
Sobre scout-v0
ar-overlay Superposición AR de especies en visor
0/9

subtareas

WebXR o Capacitor ARKit/ARCore
Frame en vivo → ID → label 3D
Presupuesto de performance: 30 fps
Deteccion WebXR Device API con fallback Capacitor ARKit/ARCore
Captura de frame de camara en tiempo real y resize a dimensiones del modelo
Identificacion por frame via modelo ONNX ligero o cascada servidor
Overlay de etiqueta 3D anclado al bounding box del organismo
Presupuesto de rendimiento: 30fps en movil gama media (Snapdragon 7xx)
Flujo tap-to-capture: congelar frame + guardar observacion desde vista AR
voice-indigenous Entrada/salida de voz en lenguas indígenas
0/9

subtareas

Whisper-tiny para STT
!
TTS custom para Zapoteco/Mixteco

Bloqueado por: No existe TTS comercial

Flujo en Zapoteco para piloto Sierra Norte
Whisper-tiny ONNX via transformers.js para STT en cliente
Deteccion de idioma y enrutamiento para Zapoteco, Mixteco, Nahuatl, Maya, Tseltal
!
Modelo TTS entrenado para Zapoteco (piloto Sierra Norte)

Bloqueado por: Requiere alianza comunitaria y corpus de voz

Flujo conversacional en Zapoteco para entrada campo por campo
Fallback a STT en espanol cuando confianza del modelo indigena es baja
Revision de gobernanza comunitaria y protocolo de consentimiento antes del despliegue
conabio-api APIs formales de alianza CONABIO/CONANP/INAH
0/9

subtareas

MOUs con cada agencia
Endpoints API con audit logs
Acuerdos de sincronización bidireccional
Borradores de MOU con equipos legales de CONABIO, CONANP e INAH
Endpoints REST API con autenticacion rst_* token
Log de auditoria estructurado para llamadas API de agencias
Protocolo de sync bidireccional: push observaciones a SNIB, pull actualizaciones de taxa
Limitacion de tasa y gestion de cuota por agencia
Adaptadores de formato: JSON Rastrum a CSV SNIB y XML CONANP

Edita docs/tasks.json para actualizar. La página se reconstruye en cada push.

Reportar un problema

Mostrar diagnósticos

Entorno

 

Errores de consola

 

Peticiones fallidas