Disenar pipelines de extraccion/sintesis con provenance tipada: invariante no hay claim sin source, conflictos marcados y escalados a humano, nunca promediados ni resueltos en silencio.
Disenar e implementar pipelines de extraccion/sintesis donde cada claim transporta su provenance tipada y donde la invariante "no hay claim sin source" es estructural, no aspiracional. [DOC] Cubre tres decisiones de ingenieria:
source[] obligatorio (id, locator, fecha). [DOC]conflict=true conservando todas las fuentes, en lugar de promediarlas o elegir una en silencio. [DOC]Resultado: un artefacto auditable donde un humano traza cualquier dato hasta su origen y ve que fuentes lo respaldan o lo contradicen. [INFERENCIA]
No la uses (anti-scope) cuando el output es prosa exploratoria sin consecuencias de decision, o cuando una sola fuente de verdad es indiscutible y no hay posibilidad de conflicto. [DOC] Forzarla ahi anade ceremonia sin valor de auditoria. [INFERENCIA]
Inputs requeridos [INFERENCIA]
source_id con su documento, version y fecha (as_of).Outputs [INFERENCIA]
Claim tipados con source[] no vacio y as_of.conflict=true y sus fuentes en conflicto.Vacio critico: si falta el inventario de fuentes o el locator de un atributo, detente y pide — no inventes un source_id. [DOC]
Claim con value, source[] no vacio y as_of. El source[] vacio debe ser invalido por construccion (tipo, schema o validacion en el constructor), no por convencion. [CÓDIGO]source_id, locator (pagina, span, celda) y fecha del documento. Ningun claim nace sin esos campos. [DOC]conflict=true y conserva todas las fuentes; no colapses a un valor unico. [CÓDIGO]conflict=true a una cola humana con ambas fuentes y la fecha visible. El pipeline no decide cual gana. [DOC]source_id y as_of junto a cada claim; los conflictos se muestran como tales, no enterrados. [DOC]source[] vacio, source_id desconocido (fuera del inventario) o conflicto resuelto en silencio. [CÓDIGO]Bundle determinista materializado en
assets/(assets/quality-rubric.json,assets/checklist.md,assets/README.md,assets/manifest.json): rubrica del guardian y checklist del gate de aceptacion. [CÓDIGO]
Los assets/scripts siguientes son el contrato deterministico previsto del skill; aun no estan materializados en el repo, por eso van tagueados
[SUPUESTO], no[CÓDIGO]. [SUPUESTO]
assets/provenance-engineering-contract.json — reportes JSON auditables. [SUPUESTO]assets/claim-source-policy.json — exige source_id, locator, as_of, source_type. [SUPUESTO]assets/conflict-policy.json — bloquea promedio, primera fuente, fuente mas reciente y cualquier resolucion silenciosa. [SUPUESTO]assets/escalation-policy.json — exige escalacion humana por conflicto. [SUPUESTO]assets/render-policy.json — muestra source, fecha y marcador de conflicto. [SUPUESTO]assets/structural-test-policy.json — convierte la invariante en test offline. [SUPUESTO]Al producir un reporte JSON de provenance, validalo con:
bash skills/provenance-engineering/scripts/check.sh # [SUPUESTO]: script previsto, aun no en repoEl validator falla si hay claims sin source, source_id desconocidos, conflictos no preservados, conflictos no escalados, fechas ocultas, tests estructurales incompletos o Guardian con pass y validacion falsa. [SUPUESTO]
from dataclasses import dataclass
from datetime import date
@dataclass(frozen=True)
class Source:
source_id: str
locator: str # page 4, cell B7, span 120-138
as_of: date
@dataclass(frozen=True)
class Claim:
attribute: str
value: str
sources: tuple[Source, ...]
conflict: bool = False
def __post_init__(self) -> None:
if not self.sources:
raise ValueError(f"claim '{self.attribute}' has no source") # GOOD: invariante por construccion
def merge(attribute: str, candidates: list[Claim]) -> Claim:
values = {c.value for c in candidates} # comparar valores ya normalizados upstream
all_sources = tuple(s for c in candidates for s in c.sources)
# GOOD: conflicto preservado con ambas fuentes, nunca promediado ni elegido en silencio
return Claim(attribute, value=" | ".join(sorted(values)), sources=all_sources,
conflict=len(values) > 1)
def assert_provenance(claims: list[Claim], known_ids: set[str]) -> None:
for c in claims:
assert c.sources, f"claim '{c.attribute}' lost its source" # GOOD: test estructural
for s in c.sources:
assert s.source_id in known_ids, f"unknown source_id {s.source_id!r}" # GOOD: rechaza id fuera de inventario# ANTI: resumen en prosa sin source_id, sin fecha, sin senal de conflicto
def summarize(records: list[dict]) -> str:
name = records[0]["name"] # ANTI: elige la primera fuente en silencio
revenue = sum(r["revenue"] for r in records) / len(records) # ANTI: promedia un conflicto
return f"{name} reported revenue of {revenue}." # sin provenance, sin as_of, conflicto borradoEsto destruye la auditabilidad: el humano no sabe de donde salio el nombre, el conflicto de cifras quedo promediado en un numero que ninguna fuente afirma, y no hay fecha. Un dato asi no sostiene una decision. [INFERENCIA]
source[] poblado con un placeholder ("varias fuentes", "interno") en vez de ids reales.conflict=true pero no enrutarlo a la cola humana (conflicto visible pero no accionado).source[] de un elemento; no fuerces conflicto. [INFERENCIA]as_of es obligatorio; si la fuente no la trae, es vacio critico, no as_of=None. [DOC]source_id fuera del inventario → falla duro; un id desconocido es indistinguible de un claim inventado. [DOC]source[] vacio o un placeholder → bloquea el output, no lo emitas. [DOC]as_of o el marcador de conflicto → no esta listo para un auditor. [INFERENCIA]El output aprueba solo si todas se cumplen: [DOC]
source[] no vacio (id + locator + fecha).source_id existe en el inventario de fuentes declarado.conflict=true y conservan todas las fuentes.as_of es visible para el humano en el render.katas-provenance-preservation. [DOC]fdad39c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.