Skip to content

Latest commit

 

History

359 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Residencia Fiscal

Qué prueba gana un caso de residencia fiscal, según los tribunales.

CI Frontend License: MIT Python 3.13 uv

residenciafiscal.org · Documentación · Contribuir


Proyecto con 106 sentencias fuente del Tribunal Supremo y la Audiencia Nacional (2015-2025) sobre residencia fiscal de personas físicas (Art. 9 LIRPF). El corpus estructurado v3 está validado por ahora sobre una muestra de cinco para consulta jurídica:

  • Criterios de residencia aplicados: 183 días, ausencias esporádicas, centro de intereses económicos y vitales, presunción familiar, tie-breaker del CDI.
  • Pruebas aportadas por AEAT y por el contribuyente, con su categoría, su peso, si el tribunal las aceptó y por qué, con cita literal y página.
  • Razonamiento judicial: doctrina citada, sobre quién recaía la carga de la prueba y si la cumplió.
  • Resultado: GANA_AEAT / GANA_CONTRIBUYENTE / PARCIAL / RETROACCION / INADMISION.

La pregunta de fondo no es qué dice la ley, sino qué acepta un juez como prueba. Eso solo se ve agregando sentencias.

El corpus de hoy es español. El pipeline no lo es: cualquier jurisdicción puede entrar si alguien aporta su jurisprudencia — ver Un país, un corpus.

Warning

La estructura jurídica la propone un agente y puede contener errores. No es asesoramiento jurídico ni fiscal. Python conserva el texto literal, hashes y validaciones; para citar una resolución, usa siempre el texto oficial del CENDOJ. Ver sentencias/AVISO_LEGAL.md.

Arquitectura

El corpus se prepara offline sin llamadas del repositorio a APIs de modelos. El gateway se reserva para responder preguntas del chat online, que permanece cerrado por defecto mientras no se autorice su despliegue.

flowchart LR
    PDFS["sentencias/<br/>PDF oficiales"] --> PY["Python<br/>texto · páginas · hashes"]
    PY --> AGENT["Agente<br/>propuesta jurídica"]
    AGENT --> GATES["Python<br/>gates · citas · compilación"]
    GATES --> CORPUS["knowledge/jurisprudencia-v3"]
    QUESTION["Pregunta del usuario"] --> NETLIFY["Netlify Function V1<br/>/api/chat · A y B en paralelo"]
    NETLIFY --> A["A · recuperación v3<br/>Luna + high"]
    CORPUS --> A
    PDFS --> B["B · Gemini File Search<br/>PDF de la muestra"]
    NETLIFY --> B
    A --> ANSWER["Dos respuestas separadas<br/>citas + coste USD"]
    B --> ANSWER
Loading
Archivo Función
src/verbatim_*.py Extracción literal por páginas y hashes
src/jurisprudence_*.py Compilación, validación y recuperación del corpus v3
src/chat_model_policy.py Política de inferencia de la estrategia A del chat
src/gateway_setup.py Clientes, uso y costes de las respuestas del chat
src/api/ Prototipo local del chat y posible runtime futuro para llamadas largas
frontend/ SPA React y Function Netlify-only V1, implementada y cerrada por configuración

La vista completa de componentes, flujos e invariantes está en docs/ARCHITECTURE.md. La guía sobre dónde debe vivir cada tipo de archivo está en docs/REPOSITORY_STRUCTURE.md.

Los dos corpus

El repositorio versiona fuentes originales y, a partir de ellas, corpus derivados y regenerables. Las fuentes nunca se editan a mano y su texto no se reescribe en ningún punto del pipeline.

Fuente Derivado Cómo se genera
sentencias/ — 106 PDF del CENDOJ knowledge/jurisprudencia-v3/ Python + agente + gates literales
normativa/es/ — 106 normas en XML del BOE knowledge/normativa/es/preceptos/ Determinista, sin LLM (make export-normativa)

Del corpus normativo se publica un Markdown por artículo, no por ley: los preceptos que deciden o prueban la residencia fiscal, más el artículo de residencia de cada uno de los 98 convenios de doble imposición firmados por España, que se guardan enteros en normativa/es/ tal como los sirve el BOE (inventario). De ahí sale el convenio que publica la página de cada país: /francia enlaza el convenio España-Francia, su artículo de residencia y el texto oficial en el BOE. Un tercer artefacto, knowledge/normativa/es/enlaces/, resuelve qué preceptos cita cada sentencia y con qué redacción del ejercicio enjuiciado. Ver docs/normativa/NORMATIVA.md.

Un país, un corpus

La residencia fiscal se decide en los tribunales de cada país, y la pregunta es la misma en todos: qué prueba acepta un juez. Lo que cambia es el articulado y quién lo interpreta.

España está publicada porque su jurisprudencia se delimitó con criterio jurídico-tributario, no porque el proyecto sea español: qué resoluciones importan, qué criterios del art. 9 LIRPF se aplican y en qué doce categorías se clasifica la prueba son decisiones de derecho tributario, no de un modelo. El pipeline es agnóstico de la jurisdicción; el criterio, no. Por eso el proyecto se nutre de la contribución de expertos en fiscalidad y tributación internacional: abogados y asesores fiscales, académicos, documentalistas jurídicos, traductores jurídicos, economistas y peritos, además de desarrolladores.

Tip

Propón tu país o escribe a info@residenciafiscal.org — cualquier jurisdicción, no solo las que ya tienen ruta en la web. No hace falta saber programar: lo que falta es criterio jurídico, no código. Página pública: residenciafiscal.org/colaborar.

Un país entra cuando existen tres cosas. Rara vez las aporta una sola persona:

Lo que hace falta Por qué
Una fuente pública oficial de resoluciones, con sus condiciones de reutilización El corpus se publica desde la fuente original y sin licencia clara no se publica. Los PDF deben llevar capa de texto: no hay OCR
El precepto nacional que decide la residencia — el equivalente al art. 9 LIRPF — y el artículo de desempate de sus convenios El análisis de una sentencia no se sostiene sin la norma que aplica
Un especialista que valide el resultado La propuesta jurídica del agente puede equivocarse. Ningún país se publica sin que un profesional del derecho tributario de esa jurisdicción lo valide

Lo que no hace falta aportar: el pipeline, la verificación de citas contra el documento fuente, el schema de extracción ni el frontend. Eso ya existe y es común a todos los países.

Dos invariantes rigen cualquier corpus nuevo, igual que el español:

  • El texto de una resolución no se reescribe. Ni se corrige, ni se completa, ni se parafrasea. Una cita solo se publica desde una subcadena exacta del texto extraído del documento oficial. Las correcciones viven en metadatos aparte.
  • Cada corpus se aísla del resto. Una consulta sobre un país no puede devolver una cita de otro, y hay tests que lo comprueban.

El criterio para arrancar el siguiente país no es el orden de llegada: es el primero que reúna una fuente reutilizable y un revisor comprometido. El proyecto lo mantiene una persona en su tiempo libre, así que no hay plazos prometidos.

El detalle operativo y la tabla de perfiles están en CONTRIBUTING.md; el estado de las páginas por país, en docs/product/COUNTRY_PAGES.md.

Puesta en marcha

Requiere uv — gestiona Python y las dependencias.

curl -LsSf https://astral.sh/uv/install.sh | sh   # si no lo tienes

make setup                  # instala Python 3.13 + dependencias en .venv
make help                   # lista todos los comandos

No hace falta activar ningún entorno: uv run lo resuelve solo.

Uso

make export-verbatim        # extrae el PDF piloto sin LLM
make export-case-v3         # compila el caso propuesto por el agente
make export-case-v3-sample  # regenera y valida la muestra de cinco
make dev                    # API + frontend en desarrollo
make dev-api                # solo API HTTP, Swagger en http://127.0.0.1:8010/docs
make fast-check             # lint + typecheck + tests

La propuesta jurídica de cada sentencia se prepara en una sesión de agente. No existe un target que envíe los PDF a OpenAI. Los comandos Python extraen, validan y compilan de forma determinista.

API

Método Ruta Descripción
GET /health Estado y frontera entre corpus y chat
GET /config Política del chat, criterios y categorías
GET /docs Swagger UI
POST /chat Comparación A/B por SSE; cerrada por defecto

La API no expone /analizar. /chat utiliza el gateway únicamente después de recuperar evidencia y exige habilitación y autenticación server-side.

Frontend

SPA React (Vite 8 + React 19 + TypeScript 7 + Tailwind 4) desplegada en Netlify: un chatbot que consulta el corpus de sentencias en lenguaje natural.

cd frontend
npm install
npm run dev         # http://127.0.0.1:5174
npm run fast-check  # lint + typecheck + tests
npm run build       # genera el corpus y compila a dist/

Note

El chat está activo en Production desde el 31 de julio de 2026: la Function Netlify-only V1 ejecuta A y B en paralelo y conserva Luna high. El rollback es volver a stub. La activación técnica no cierra la privacidad ni la revisión jurídica del corpus, que siguen pendientes. El recorrido Edge → FastAPI se conserva como opción futura para llamadas de más de 60 s. Runbook: docs/operations/CHAT_DEPLOYMENT.md.

Errores en producción (Sentry)

Sentry instrumenta los tres runtimes, cada uno en su propio proyecto: la API FastAPI (src/api/sentry_config.py), la SPA React (frontend/src/lib/sentry-runtime.ts) y la Netlify Function del chat (frontend/netlify/functions/chat/observability.ts).

Los tres se inicializan solo con la telemetría habilitada y un DSN presente, envían sendDefaultPii: false y borran cabeceras, cookies y cuerpo de la petición antes de enviar el evento: una pregunta del chat es dato fiscal y no debe viajar a un servicio de errores. La suite de pytest no instrumenta nada, aunque el .env local tenga la telemetría encendida, porque provoca excepciones a propósito.

La Function no usa el SDK: construye el envelope con fetch y envía un evento sintético con código de fallo, etapa, request_id y nombre de clase del error. Nunca viaja la pregunta, la respuesta ni el message de la excepción del proveedor. El coste se sigue observando por chat_cost_reconciled en los logs y por el resumen diario del ledger, no por Sentry.

Las incidencias accionables de los tres proyectos entran en el mismo autofix aislado y con gates que Presupuestor; arquitectura y guardrails en docs/operations/AUTOFIX.md.

Variable Ámbito Nota
SENTRY_ENABLED, SENTRY_BACKEND_DSN API Sin ambas, la API no inicializa Sentry
SENTRY_ENVIRONMENT, SENTRY_RELEASE, SENTRY_TRACES_SAMPLE_RATE API Entorno, versión y muestreo de trazas
VITE_SENTRY_ENABLED, VITE_SENTRY_DSN Frontend Todo VITE_* es público en el bundle; el DSN identifica el proyecto, no es un token
VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_TRACES_SAMPLE_RATE Frontend Equivalentes de navegador
CHAT_SENTRY_ENABLED, CHAT_SENTRY_DSN Netlify Function Proyecto residencia-fiscal-chat. Runtime de servidor: nunca con prefijo VITE_. La cuenta Legacy no admite scope Functions; van como variables ordinarias de production
SENTRY_ORG_SLUG, SENTRY_TOKEN Build Solo para subir sourcemaps. Nunca con prefijo VITE_

Nombres y valores de ejemplo, en .env.example.

Documentación

Documento Contenido
docs/README.md Índice temático de toda la documentación
docs/ARCHITECTURE.md Componentes, flujos, límites e invariantes
docs/REPOSITORY_STRUCTURE.md Convenciones de carpetas y rutas
CLAUDE.md Guía operativa, comandos, costes y troubleshooting
CONTRIBUTING.md Entorno, gates de CI y qué se espera de un PR
SECURITY.md Cómo reportar una vulnerabilidad y qué está en el alcance

Licencia

Código y documentación bajo licencia MIT.

Los documentos jurídicos que el repositorio incluye no están cubiertos por esa licencia; la salvedad completa está en NOTICE.md. Cada corpus se rige por las condiciones de reutilización de su fuente:

  • Las resoluciones judiciales de sentencias/ son documentos públicos del CENDOJ, publicados ya pseudonimizados — sentencias/AVISO_LEGAL.md.
  • Los textos legales de normativa/ proceden del BOE, cuya edición oficial es la única versión con valor jurídico — normativa/es/AVISO_LEGAL.md.

Fuentes

About

Análisis con LLMs de 106 sentencias del TS y la AN sobre residencia fiscal (Art. 9 LIRPF): criterios aplicados, pruebas aceptadas y rechazadas, y razonamiento judicial

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages