Skip to content

Available now

Run a whole story ​

One accepted message proves your serialiser works. A whole story proves your state handling works, and that's where most integrations break: every snapshot is checked against the one before it. Why, and the rules in brief: Snapshots, not deltas.

The sequence rules ​

Build every snapshot from your complete current state of the story, never from what changed.

RuleWhyBroken
Include every top-level member you've sent beforeA missing member erases it for every consumer409 snapshot.members_missing
Keep every asset that's still part of the storyA dropped asset disappears downstream409 snapshot.assets_dropped
Never change an asset's asset_typeAn asset_id means one thing for its whole life409 asset.type_changed
sequence_number strictly increasesConsumers keep the highest. An equal value means two writers collided409 sequence_number.not_increasing
updated_at never goes backwardsThe same, in time409 updated_at.backwards
A KILLED, SPIKED or ARCHIVED story stays terminalA dead story doesn't come back409 story_type.left_terminal
Keep one correlation_id for the storyIt keeps the story in order for every consumerAccepted, with correlation_id.changed under tolerated

Two more things the bus allows:

  • A gap in sequence_number (5, then 7): legal, but a snapshot may be missing. Reported under tolerated in the 202 (sequence_number.gap).
  • lifecycle disappearing when a story leaves ACTIVE: the schema requires that, so it isn't counted as a missing member. Nothing is reported.

Each snapshot is a new message with a new message_id. Reusing one for a different message is 409message_id.reused.

Walk a breaking story ​

Publish the seven snapshots of the published hurricane run, in order, as one story: your own story_id, one correlation_id, a new message_id each. Every one should be accepted with 202.

SeqWhat changes
1Hurricane approaching: ACTIVE, phase DEVELOPING
2Phase BREAKING; a press-conference feed asset is added while still capturing
3The feed is captured and ready
4Upgraded: new headline, higher priority, a new source
5An AI transcript asset, derived from the feed
6A claim extracted as an assertion; a lower-third asset behind a pending standards gate
7The claim confirmed: the gate approved, a new headline

Then open the story on the portal's Story timeline (see Send your first message). Select a snapshot to see what changed since the previous one. It should show only what you meant to change: an unintended difference is a bug in your state handling, even though the bus accepted it.

Now do the same through your product: create the story in its UI or API, make the same edits, and let it publish.

End a story ​

SendExpect
The next snapshot with story_type: KILLED (and no lifecycle)202
The one after with story_type: ACTIVE409 story_type.left_terminal
The one after with story_type: ARCHIVED202: moving between terminal states is allowed

Messages of other families ​

Messages about a story share its correlation_id, whatever their family. That keeps them in order with the story's snapshots for every consumer.

FamilyTie it to the story withTopic, by convention
link.*The story's correlation_id; an asset_id from the snapshotsom.link.committed
telling.*The story's correlation_id; the link_id it tellssom.telling.started
delivery.media_availableThe story's correlation_id when the story is knownsom.delivery.media_available
skill.warning.raisedThe story's correlation_id; causation_id = the snapshot's message_idsom.skill.warning.raised
system.auditThe story's correlation_idsom.system.audit
  • topic only has to start with som.. The bus routes on message_type, never on topic, though a consumer can ask for a topic-prefix filter.
  • Set causation_id whenever your message reacts to another one.
  • Only story.context is checked against earlier messages. Pairs such as a telling's start and end aren't checked by the bus, but publishers will expect them to be coherent.

Order and concurrency ​

  • Wait for the answer before you send the next message about the same story. The bus keeps a story in the order it accepts messages: fire two snapshots of one story in parallel, and the second may be accepted first and the other refused as stale.
  • Different stories can go in parallel.
  • One writer per story. The first system to publish a story owns its snapshots, usually the NCS; other systems react with their own families. A snapshot from another system gets story.not_owner: a warning by default, a 409 if your workspace chooses. A workspace can list hand-offs, such as NCS to web CMS (Story ownership). Two writers racing on one story also get 409sequence_number.not_increasing or commit.contention.

Tests and reruns ​

The bus remembers where every story in your workspace stands (its sequence state, not the payload) with no expiry, so a rerun needs a new story_id. Started again under an old one, snapshot 1 is refused as stale.

Checkpoint ​

  • [ ] My product published a whole story, and every snapshot was accepted.
  • [ ] The portal's diff between snapshots shows only the changes I intended.
  • [ ] A terminal story can't be revived by my product, and the 409 is handled.
  • [ ] Every other message I produce for a story carries its correlation_id; reactions carry causation_id.
  • [ ] My product never sends two messages for the same story at once.

Next: Handle the bus's answers.

SOM is an open standard maintained by the SOM working group. This service is not endorsed by it.