Construir

Primeiros passos

Leia os documentos curtos, rode o corpus de conformidade, valide sua própria mensagem com a validação de formatos ligada e publique. Depois escolha um nível e converse com o grupo de trabalho.

Passo 2

Rode o corpus de conformidade

O repositório da especificação traz exemplos completos que precisam validar, vinte casos negativos que precisam ser rejeitados e ferramentas em Python puro que verificam 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
Quatro perguntas diferentes
  • Esta mensagem é válida? — validate.py
  • Os schemas de fato restringem? — validate_negative.py. Dezenove casos falham nos schemas; o vigésimo, som_version: "0.3.2", falha na regra de versão do fio, que a sua implementação precisa aplicar por conta própria.
  • O schema corresponde ao que diz sobre si mesmo? — som_lint.py
  • Uma sequência de snapshots se sustenta? — validate_sequence.py, onde moram os bugs caros.

Instale o rfc3339-validator. Sem ele, o jsonschema do Python aceita em silêncio um timestamp malformado. O corpus negativo inclui esse caso justamente para que a verificação falhe ruidosamente quando o pacote estiver faltando.

Passo 3

Valide na sua própria stack — com os formatos validados

Duas implementações que discordam sobre se message_id precisa ser um UUID não são interoperáveis. Em Node, isso significa o build 2020-12 do Ajv mais 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, não uma implementação de referência — guarde em cache os validadores compilados em vez de buscá-los a cada mensagem. Os schemas são servidos na URL do seu próprio $id, então resolver pelo identificador também funciona.

Passo 4

Publique um snapshot de história 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"
  }
}
Checklist para cada snapshot
  • Um message_id UUID novo (v7 recomendado); o mesmo correlation_id ao longo do ciclo de vida da história.
  • O mesmo story_id, um sequence_number maior, um updated_at posterior.
  • Todos os campos, inclusive os que outros sistemas contribuíram. Omitir significa ausente.
  • lifecycle presente se, e somente se, story_type for ACTIVE.
  • O seu próprio originating_system, carimbado de novo a cada publicação.
  • Tudo o que o padrão não carrega vai em extensions["com.{vendor}.…"].

Veja numa pipeline ao vivo. O emulador ↗ do projeto é um painel sobre uma pipeline em funcionamento que você mesmo pode usar, e as demos da IBC ↗ mostram dois cenários completos de ponta a ponta. Uma implementação de referência em .NET virá num repositório separado; o starter do hackathon pré-1.0 emite 0.3.2 e não é tráfego 1.0.

Adesão

Nada do seu produto precisa sair

Um sistema basta para começar. As integrações são descritas em três níveis.

1

Assinar e publicar

Leia a história e coloque a sua própria família na pipeline. O projeto estima isso em dias, não em meses.

2

Contexto bidirecional

Sua ferramenta consome e também contribui com contexto estruturado — assertions, proveniência, links, tellings.

3

Decidir com base na história

Sua ferramenta age de forma autônoma sobre o que lê, por meio de um executor que aplica as skills da casa.