Sistema de Provenance

Sistema de trazabilidad de código para identificar y documentar qué código fue escrito por IA y qué por humanos.

Estado: Funcional desde v0.2.0 (Fase 2 completada).


Visión general

El sistema de provenance responde a la pregunta: ¿quién escribió este código? Analiza el historial de git y logs de sesión de agentes IA para clasificar cada archivo como ai, human o mixed, con un score de confianza entre 0.0 y 1.0.

licit trace --stats
Analyzing git history...
Records: 45 files analyzed
AI-generated: 18 (40.0%)
Human-written: 22 (48.9%)
Mixed: 5 (11.1%)

AI tools detected: claude-code (15), cursor (3)
Models detected: claude-sonnet-4 (12), claude-opus-4 (3), gpt-4o (3)

Stored in .licit/provenance.jsonl

Arquitectura

El sistema se compone de 8 módulos en src/licit/provenance/:

provenance/
├── heuristics.py          # Motor de 6 heurísticas de detección AI
├── git_analyzer.py        # Parser de git log con análisis heurístico
├── store.py               # Store JSONL append-only
├── attestation.py         # Firmado HMAC-SHA256 + Merkle tree
├── tracker.py             # Orquestador del pipeline completo
├── report.py              # Generador de reportes Markdown
└── session_readers/
    ├── base.py            # Protocol SessionReader
    └── claude_code.py     # Reader para Claude Code

Flujo de datos

Git Log ──→ GitAnalyzer ──→ ProvenanceRecord[] ─┐
                                                 ├──→ ProvenanceTracker ──→ Store
Session Logs ──→ SessionReader ──→ Records[] ───┘          │
                                                           ├──→ Attestation (HMAC + Merkle)
                                                           └──→ Report (Markdown)

Motor de heurísticas

AICommitHeuristics aplica 6 heurísticas independientes a cada commit de git. Cada heurística produce un score (0.0-1.0) y un peso relativo.

Heurísticas

#NombrePesoQué detecta
H1Author pattern3.0Nombres de autor AI: claude, copilot, cursor, bot, devin, aider, [bot]
H2Message pattern1.5Patrones de commit: conventional commits, [ai], implement, generate, Co-authored-by en subject
H3Bulk changes2.0Cambios masivos: >20 archivos y >500 líneas en un solo commit
H4Co-author3.0Trailer Co-authored-by: con keywords AI en el body del commit
H5File patterns1.0Todos los archivos modificados son test files (test_, _test., .spec.)
H6Time pattern0.5Commits entre 1:00 AM y 5:00 AM

Cálculo del score

Solo las heurísticas que producen señal (score > 0) participan en el promedio ponderado:

signaling = [h for h in results if h.score > 0]
total_weight = sum(h.weight for h in signaling)
final_score = sum(h.score * h.weight for h in signaling) / total_weight

Si ninguna heurística señaliza, el score final es 0.0 (humano).

Clasificación

ScoreClasificación
>= 0.7ai — Código probablemente generado por IA
>= 0.5mixed — Código con contribución mixta
< 0.5human — Código probablemente humano

El umbral configurable confidence_threshold (default: 0.6) afecta al filtrado en reportes, no a la clasificación base.


Git Analyzer

GitAnalyzer parsea el historial de git y aplica las heurísticas a cada commit.

Parsing de git log

Ejecuta git log con un formato personalizado usando separadores hexadecimales (%x00, %x01) para parsear campos de forma robusta:

git log --format="%x00%x01H%x01an%x01ae%x01aI%x01s%x01b" --numstat

Campos extraídos en CommitInfo:

Opciones

OpciónCLI flagDescripción
since--sinceAnalizar commits desde fecha (YYYY-MM-DD) o tag
Timeout30 segundos para git log (previene bloqueos en repos masivos)

Resultado por archivo

Para cada archivo, se toma el score máximo entre todos los commits que lo modificaron. El método de detección es siempre ProvenanceSource.GIT_INFER.


Session Readers

Los session readers extraen información de proveniencia directamente de los logs de sesión de agentes IA.

Protocol

class SessionReader(Protocol):
    def can_read(self, path: Path) -> bool: ...
    def read_sessions(self, path: Path) -> list[ProvenanceRecord]: ...

Claude Code Reader

Lee archivos JSONL de sesión de Claude Code (típicamente en ~/.claude/projects/).

Campos extraídos:

Configuración:

provenance:
  methods:
    - git-infer
    - session-log
  session_dirs:
    - ~/.claude/projects/

Extensibilidad

Para añadir soporte para otro agente (ej. Cursor), implementar el Protocol SessionReader y registrarlo en ProvenanceTracker.


Store JSONL

ProvenanceStore almacena registros de proveniencia en formato JSONL (JSON Lines) append-only.

Formato

Cada línea es un objeto JSON independiente:

{"file_path": "src/app.py", "source": "ai", "confidence": 0.85, "method": "git-infer", "timestamp": "2026-03-10T14:30:00", "model": "claude-sonnet-4", "agent_tool": "claude-code"}
{"file_path": "tests/test_app.py", "source": "human", "confidence": 0.0, "method": "git-infer", "timestamp": "2026-03-10T14:30:00"}

Operaciones

OperaciónMétodoDescripción
Appendappend(records)Añade registros al final del archivo
Loadload()Lee todos los registros del store
Countcount()Cuenta registros sin cargar todo en memoria
Clearclear()Vacía el store (para re-análisis)

Características


Attestation (Firmado criptográfico)

ProvenanceAttestor proporciona firmado HMAC-SHA256 individual y verificación batch con Merkle tree.

Firmado individual

from licit.provenance.attestation import ProvenanceAttestor

attestor = ProvenanceAttestor()  # Auto-genera key si no existe

# Firmar un registro
data = {"file": "app.py", "source": "ai", "confidence": 0.85}
signature = attestor.sign_record(data)

# Verificar
assert attestor.verify_record(data, signature)

Merkle tree (batch)

Para verificar integridad de un conjunto de registros:

records = [record1, record2, record3, record4]
root_hash = attestor.sign_batch(records)
         root_hash
        /         \
    hash_01      hash_23
    /    \       /    \
 hash_0 hash_1 hash_2 hash_3
   |      |      |      |
 rec_0  rec_1  rec_2  rec_3

Gestión de claves

La clave de firmado se resuelve en este orden:

  1. Path explícito en config: provenance.sign_key_path
  2. Fallback local: .licit/.signing-key en el proyecto
  3. Auto-generación: 32 bytes aleatorios con os.urandom(32)
# Ejemplo con clave explícita
provenance:
  sign: true
  sign_key_path: ~/.licit/signing-key

Tracker (Orquestador)

ProvenanceTracker orquesta el pipeline completo:

from licit.provenance.tracker import ProvenanceTracker

tracker = ProvenanceTracker(config=config, project_root="/path/to/project")
stats = tracker.run(since="2026-01-01")

Pipeline

  1. Git analysis: Ejecuta GitAnalyzer para analizar commits
  2. Session reading: Lee logs de sesión si session-log está en methods
  3. Merge: Combina resultados de git y sesiones (prioridad a sesiones si hay conflicto)
  4. Signing: Firma cada registro si sign: true
  5. Storage: Almacena en JSONL via ProvenanceStore
  6. Stats: Retorna estadísticas agregadas

Estadísticas retornadas

{
    "total_files": 45,
    "ai_count": 18,
    "human_count": 22,
    "mixed_count": 5,
    "ai_percentage": 40.0,
    "human_percentage": 48.9,
    "mixed_percentage": 11.1,
    "tools_detected": {"claude-code": 15, "cursor": 3},
    "models_detected": {"claude-sonnet-4": 12, "claude-opus-4": 3, "gpt-4o": 3},
}

Report

ProvenanceReportGenerator genera reportes Markdown a partir de los registros almacenados.

Contenido del reporte

  1. Resumen: Totales y porcentajes por clasificación
  2. Tabla detallada: Archivo, fuente, confianza, método, modelo, herramienta
  3. Herramientas detectadas: Frecuencia de cada agente IA
  4. Modelos detectados: Frecuencia de cada modelo

Generación

licit trace --report
# Genera .licit/reports/provenance.md
from licit.provenance.report import ProvenanceReportGenerator

generator = ProvenanceReportGenerator()
markdown = generator.generate(records, project_name="mi-proyecto")

Configuración completa

provenance:
  enabled: true
  methods:
    - git-infer              # Heurísticas de git history
    - session-log            # Logs de sesión de agentes
  session_dirs:
    - ~/.claude/projects/    # Directorio con logs de Claude Code
  sign: true                 # Firmar registros con HMAC-SHA256
  sign_key_path: ~/.licit/signing-key
  confidence_threshold: 0.6  # Umbral de confianza
  store_path: .licit/provenance.jsonl

Integración con compliance

La evidencia de provenance alimenta directamente el EvidenceBundle:

Campo del bundleQué aporta provenance
has_provenanceTrue si existe store con registros
provenance_statsEstadísticas agregadas (totales, porcentajes, herramientas, modelos)

Estos campos son evaluados por los frameworks de compliance:


Testing

167 tests cubren el sistema de provenance:

MóduloTestsArchivo
Heurísticas23tests/test_provenance/test_heuristics.py
Git Analyzer15tests/test_provenance/test_git_analyzer.py
Store15tests/test_provenance/test_store.py
Attestation13tests/test_provenance/test_attestation.py
Tracker7tests/test_provenance/test_tracker.py
Session Reader13tests/test_provenance/test_session_reader.py
QA Edge Cases81tests/test_provenance/test_qa_edge_cases.py
Total167

Los tests incluyen: