Construir
Primeros pasos
Lee los documentos breves, ejecuta el corpus de conformidad, valida tu propio mensaje con la validación de formatos activada y publícalo. Después elige un nivel y habla con el grupo de trabajo.
Paso 1
Lee cuatro cosas, en este orden
Introducción ↗
Por qué existe SOM, los tres sustantivos y los seis principios.
02 · para implementadoresConformidad ↗
Qué significa ser conforme con SOM 1.0 — más breve de lo que esperas.
03 · la imagen más claraEl caso del huracán ↗
Una Story contada a lo largo de siete snapshots.
04 · tu primera familiaSchema story-context ↗
La familia que casi con toda seguridad implementarás primero.
Paso 2
Ejecuta el corpus de conformidad
El repositorio de la especificación incluye ejemplos prácticos que deben validar, veinte casos negativos que deben rechazarse y herramientas en Python puro que comprueban ambos.
git clone https://github.com/storyobjectmodel/som.git
cd som
pip install jsonschema rfc3339-validator
python3 tools/validate.py # every example is legal
python3 tools/validate_negative.py # every negative case is rejected
python3 tools/som_lint.py schema # schemas agree with their own claims
python3 tools/validate_sequence.py examples/hurricane-run
# a story over time holds together- ¿Es válido este mensaje? —
validate.py - ¿Restringen de verdad los schemas? —
validate_negative.py. Diecinueve casos fallan en los schemas; el vigésimo,som_version: "0.3.2", falla por la regla de versión en el cable, que tu implementación debe aplicar por su cuenta. - ¿Coincide el schema con lo que dice de sí mismo? —
som_lint.py - ¿Se sostiene una serie de snapshots? —
validate_sequence.py, donde viven los errores caros.
Instala rfc3339-validator. Sin él, el jsonschema de Python acepta en silencio un timestamp mal formado. El corpus negativo incluye ese caso precisamente para que la comprobación falle de forma ruidosa cuando falte el paquete.
Paso 3
Valida en tu propio stack — con los formatos validados
Dos implementaciones que no se ponen de acuerdo en si message_id debe ser un UUID no son interoperables. En Node, eso significa la versión 2020-12 de Ajv más ajv-formats.
// npm i ajv ajv-formats
import Ajv2020 from 'ajv/dist/2020';
import addFormats from 'ajv-formats';
const BASE = 'https://storyobjectmodel.com/schema/1.0';
const FAMILY_SCHEMA: Record<string, string> = {
'story.context': 'story-context',
'skill.warning.raised': 'skill-warning',
'delivery.media_available': 'delivery-media-available',
'link.committed': 'link-event', 'link.gate_changed': 'link-event', 'link.withdrawn': 'link-event',
'telling.started': 'telling-event', 'telling.ended': 'telling-event', 'telling.exposed': 'telling-event',
'system.audit': 'system-audit',
};
const ajv = new Ajv2020({ strict: false, allErrors: true });
addFormats(ajv); // conformance §4: uuid and date-time MUST be asserted
const load = async (name: string) => (await fetch(`${BASE}/${name}.schema.json`)).json();
export async function validateMessage(msg: { som_version?: string; message_type?: string; payload?: unknown }) {
const envelope = ajv.compile(await load('envelope'));
if (!envelope(msg)) return { ok: false, errors: envelope.errors };
// §3: 0.3.2 is not SOM 1.0 — and never branch on the version otherwise.
if (msg.som_version?.startsWith('0.')) return { ok: false, errors: ['pre-1.0 som_version'] };
// §2 and §5: dispatch on message_type only; ignore families you don't handle.
const family = FAMILY_SCHEMA[msg.message_type ?? ''];
if (!family) return { ok: true, ignored: true };
const payload = ajv.compile(await load(family));
return payload(msg.payload) ? { ok: true } : { ok: false, errors: payload.errors };
}Ilustrativo, no una implementación de referencia — guarda en caché los validadores compilados en lugar de descargar los schemas en cada mensaje. Los schemas se sirven en la URL de su propio $id, así que resolverlos por identificador también funciona.
Paso 4
Publica un snapshot de Story conforme
{
"som_version": "1.0.0",
"message_id": "0199a1c4-7a2e-7b31-8c55-4d2f9e6a1b07",
"correlation_id": "0199a1c4-0000-7000-8000-000000000911",
"message_type": "story.context",
"timestamp": "2026-09-11T16:41:00.000000Z",
"originating_system": {
"system_id": "ncs-nyc-01", "system_type": "ncs"
},
"topic": "som.story.context.hurricane-2026-0911",
"payload": {
"story_id": "hurricane-2026-0911",
"slug": "HURRICANE-LANDFALL",
"headline": "Hurricane makes Cat 4 landfall",
"story_type": "ACTIVE",
"lifecycle": { "phase": "BREAKING",
"phase_entered_at": "2026-09-11T14:00:00Z" },
"sequence_number": 7,
"updated_at": "2026-09-11T16:41:00Z"
}
}- Un
message_idUUID nuevo (se recomienda v7); el mismocorrelation_iddurante todo el ciclo de vida de la Story. - El mismo
story_id, unsequence_numbermayor, unupdated_atposterior. - Todos los campos, incluidos los que aportaron otros sistemas. Omitir significa ausente.
lifecyclepresente si y solo sistory_typeesACTIVE.- Tu propio
originating_system, vuelto a sellar en cada publicación. - Todo lo que el estándar no lleva va en
extensions["com.{vendor}.…"].
Míralo en un bus en vivo. El emulador ↗ del proyecto es un panel sobre un bus en funcionamiento que puedes usar tú mismo, y las demos del IBC ↗ muestran dos escenarios completos de principio a fin. Una implementación de referencia en .NET llegará en un repositorio aparte; el kit de inicio del hackathon, anterior a 1.0, emite 0.3.2 y no es tráfico 1.0.
Unirse
No tienes que sacar nada de tu producto
Basta un sistema para empezar. Las integraciones se describen en tres niveles.
Suscribirse y publicar
Lee la Story y pon tu propia familia en el bus. El proyecto lo calcula en días, no en meses.
Contexto bidireccional
Tu herramienta consume y también aporta contexto estructurado — assertions, procedencia, links, Tellings.
Decidir según la Story
Tu herramienta actúa de forma autónoma sobre lo que lee, mediante un executor que aplica las skills de la casa.
Participa
El grupo de trabajo está abierto
Manifiesta tu interés ↗
Desarrolla sobre él, escribe skills, organiza una sesión de Story Archaeology, únete al grupo de trabajo o simplemente sigue el trabajo — el formulario oficial pregunta quién eres y qué quieres hacer.
formulario de storyobjectmodel.com →Propón un cambio ↗
Todo cambio llega como pull request, y cualquiera puede abrir uno. Los cambios de schema están sujetos a la política de compatibilidad; las preguntas abiertas viven en el registro abierto.
GOVERNANCE · CONTRIBUTING →Consulta una integración
Para todo lo que el README del repositorio no responda, escribe al proyecto.
hello@storyobjectmodel.com →Los schemas mandan. Cuando cualquier texto — este sitio incluido — contradice un schema publicado, el schema tiene razón. Explora las siete familias →