LineageResolution lineage.resolution@1
Generated at from
openvibe-contracts v0.33.0 and
openvibe-sdk v0.5.0.
- Version
- 1.0.0
- Owner
- live
- Visibility
- first-party
- Status
- active
- Compatibility
- backward
- Schema
https://openvibe.network/contracts/lineage/resolution.v1.json
The canonical channel/owner resolver's answer to lineage.resolve-request@1 (roadmap §15.10, requirement D20; capability live.lineage.resolve). One resolver serves channel, stream, VOD, clip, Pulse and creator-UI callers. PRECEDENCE between inputs (the roadmap's order): 1 explicit_slug (slug), 2 nested_slug (slug <channel>/<slot>, or parent_slug), 3 channel_id, 4 stream_lookup (stream_id, then slot_id), 5 the record's own lineage (clip_id, then vod_id), 6 media_lineage (media_object_id), 7 owner_subject, 8 legacy_map (legacy_ids). The first input in that order that resolves decides (resolved_by). Every other input that resolves must name the same channel, otherwise the answer is unresolved with reason conflict. An input whose source could not be asked makes the answer unresolved with reason source_unavailable, since it might have disagreed. An input that names nothing is reported not_found in checked and does not block the others. LINEAGE inside a record, first link that answers wins: a stream belongs to its owner, and a slot too; a VOD goes to its stream, then to its slot when the stream row is gone, then to the owner recorded on the VOD (legacy_metadata); a clip goes to its stream, then to its parent VOD's lineage (vod_parent), then to the channel recorded on the clip (legacy_metadata), and never to the clipper; a Media object goes to the record its legacy_ref names (media_lineage), then to its owner subject, then to the owner user recorded on it (legacy_metadata); a legacy:<app>:<kind>:<id> reference is read as that record (legacy_map). CONFIDENCE is the weakest hop: exact when the input itself names the channel, a stream or slot row, or the owner subject; derived when the answer came through a parent record or an owner recorded on a record; legacy_map when it needed a legacy id map (legacy_ids, a legacy media reference). A display name never resolves anything. Unresolved answers carry no channel.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
status | enum | yes |
| |
reason | enum | Why nothing resolved. no_input: the request named nothing. display_name_only: only a display name was given. not_found: no input names anything that exists. conflict: two inputs name different channels. ambiguous: an input names more than one channel (an owner subject linked to two accounts). source_unavailable: a record could not be read (Media down); retry later. |
| |
detail | string |
| ||
channel | object |
| ||
channel.id | string | yes | The channel's id at its owner (Live: channels.id). |
|
channel.slug | string | yes | The channel's current slug (Live: the username, /@<slug>). Slugs can change; id and owner_subject do not. |
|
channel.owner_subject | string | null | yes | The owner's canonical subject, or null while the owner has not signed in through OpenVibe.Network since subjects were issued. |
|
channel.legacy_ids | object | The owner's pre-subject ids, for callers that still key by them. |
| |
channel.legacy_ids.live_user_id | integer |
| ||
channel.legacy_ids.network_user_id | integer |
| ||
stream | object |
| ||
stream.id | string | null | yes | The broadcast session, or null when only a slot was named. | |
stream.slot_id | string | null | |||
stream.slot_slug | string | null | |||
stream.missing | boolean | true when a record names this stream but the stream row no longer exists. | ||
vod | object |
| ||
vod.id | string | yes | ||
vod.stream_id | string | null | |||
vod.slot_id | string | null | |||
clip | object |
| ||
clip.id | string | yes | ||
clip.vod_id | string | null | |||
clip.stream_id | string | null | |||
media_object | object |
| ||
media_object.id | string | yes |
| |
media_object.kind | string | null | |||
media_object.legacy_ref | string | null | |||
resolved_by | enum | The input that decided. |
| |
rule | enum | The lineage rule that answered for the deciding input. |
| |
confidence | enum |
| ||
via | array of string | The hops the deciding input took, e.g. ["clip:7", "vod:42", "slot:3", "user:17"]. |
| |
checked | array of object | What each input given came to. |
| |
checked[].input | enum | yes |
| |
checked[].outcome | enum | yes |
| |
checked[].channel_slug | string | The channel this input named, when it named one. |
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: clip-vod-stream-channel
{
"status": "resolved",
"channel": {
"id": "12",
"slug": "alice",
"owner_subject": null,
"legacy_ids": {
"live_user_id": 17
}
},
"stream": {
"id": "9",
"slot_id": "3",
"slot_slug": "garage-cam"
},
"vod": {
"id": "42",
"stream_id": "9",
"slot_id": "3"
},
"clip": {
"id": "7",
"vod_id": "42",
"stream_id": null
},
"resolved_by": "clip_id",
"rule": "vod_parent",
"confidence": "derived",
"via": [
"clip:7",
"vod:42",
"stream:9",
"user:17"
],
"checked": [
{
"input": "clip_id",
"outcome": "decided",
"channel_slug": "alice"
}
]
}Valid: deleted-stream-surviving-vod
{
"status": "resolved",
"channel": {
"id": "12",
"slug": "alice",
"owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"stream": {
"id": "9",
"slot_id": "3",
"slot_slug": "garage-cam",
"missing": true
},
"vod": {
"id": "42",
"stream_id": "9",
"slot_id": "3"
},
"resolved_by": "vod_id",
"rule": "stream_lookup",
"confidence": "derived",
"via": [
"vod:42",
"slot:3",
"user:17"
]
}Valid: legacy-media-ref
{
"status": "resolved",
"channel": {
"id": "12",
"slug": "alice",
"owner_subject": null
},
"vod": {
"id": "42",
"stream_id": "9",
"slot_id": null
},
"media_object": {
"id": "legacy:live:vod:42",
"kind": "vod",
"legacy_ref": "legacy:live:vod:42"
},
"resolved_by": "media_object_id",
"rule": "legacy_map",
"confidence": "legacy_map",
"via": [
"media:legacy:live:vod:42",
"vod:42",
"stream:9",
"user:17"
]
}Valid: slug
{
"status": "resolved",
"channel": {
"id": "12",
"slug": "alice",
"owner_subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"legacy_ids": {
"live_user_id": 17,
"network_user_id": 57
}
},
"resolved_by": "slug",
"rule": "explicit_slug",
"confidence": "exact",
"via": [
"user:17"
],
"checked": [
{
"input": "slug",
"outcome": "decided",
"channel_slug": "alice"
}
]
}Valid: unresolved-conflict
{
"status": "unresolved",
"reason": "conflict",
"checked": [
{
"input": "slug",
"outcome": "decided",
"channel_slug": "mallory"
},
{
"input": "vod_id",
"outcome": "conflict",
"channel_slug": "alice"
}
]
}Valid: unresolved-display-name
{
"status": "unresolved",
"reason": "display_name_only",
"detail": "a display name is never sufficient to establish a channel or its owner",
"checked": [
{
"input": "display_name",
"outcome": "ignored"
}
]
}Rejected: local-owner-subject
{
"status": "resolved",
"channel": {
"id": "12",
"slug": "alice",
"owner_subject": "17"
},
"resolved_by": "slug",
"rule": "explicit_slug",
"confidence": "exact"
}Rejected: resolved-without-channel
{
"status": "resolved",
"resolved_by": "slug",
"rule": "explicit_slug",
"confidence": "exact"
}Rejected: unknown-confidence
{
"status": "resolved",
"channel": {
"id": "12",
"slug": "alice",
"owner_subject": null
},
"resolved_by": "display_name",
"rule": "explicit_slug",
"confidence": "guessed"
}Rejected: unresolved-with-channel
{
"status": "unresolved",
"reason": "conflict",
"channel": {
"id": "12",
"slug": "alice",
"owner_subject": null
}
}Rejected: unresolved-without-reason
{
"status": "unresolved"
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('lineage.resolution@1', value); // { valid, errors: [{ path, message }] }