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]JaviMontano/jm-adk-beta
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.