Relationships and assurance
Explicit edges, the three assurance levels, and exactly where a machine proposal's confidence score and method live in the payload.
Most edges in an OWG graph are never written by hand. They fall out of running the work: a task that declares used and produced has stated its own lineage. Those are structural edges, and they cost nothing.
This page is about the other kind — edges that require judgement to assert, including every edge a machine proposes while federating two systems together. Those need somewhere to live and something to say about how much they should be trusted.
Structural versus explicit
| Structural | Explicit | |
|---|---|---|
| Where | Implicit in tasks[] — used, produced, performed_by, ran_on, depends_on | The relationships[] array |
| Cost | Free. A by-product of describing work | Requires intelligence or a human |
| Assurance | Always asserted by the executing system | Any of three levels |
| Examples | This task consumed that plate | This asset is the same as that one in Frame.io |
The rule: if executing the workflow proves the edge, it is structural. If something had to decide, it is explicit. Never duplicate a structural edge in relationships[].
The relationship object
{
"relationships": [
{
"id": "rel_0001",
"relation": "same_as",
"from": { "kind": "asset", "id": "plate_0140" },
"to": { "kind": "asset", "id": "frameio_b2c8e9f1" },
"assurance": "inferred",
"confidence": 0.82,
"method": "m-corroborated-2",
"proposed_by": "connector_frameio",
"proposed_at": "2026-08-20T09:14:00Z"
}
]
}| Field | Type | Notes |
|---|---|---|
id | string | Required. Identifies the edge, so a decision about it can be recorded |
relation | string | Required. The predicate. See vocabulary |
from | object | Required. { kind, id } |
to | object | Required. { kind, id } |
assurance | enum | Required. attested | asserted | inferred |
confidence | number | 0.0–1.0. Required when assurance is inferred; forbidden otherwise |
method | string | Required when assurance is inferred; forbidden otherwise |
proposed_by | string | Participant id — the connector, agent, or person proposing |
proposed_at | string | RFC 3339 timestamp |
accepted_by | string | Participant id of whoever confirmed it. Absent while pending |
accepted_at | string | RFC 3339 timestamp of acceptance |
metadata | object | Freeform |
profiles | object | Profile-contributed extensions, keyed by profile id (v0.92). See Extensions |
kind in an endpoint is one of asset, participant, task, organization, infrastructure, context.
The conditional requirement is enforced by the schema, not merely described: an inferred relationship without confidence and method is a validation error, and an attested or asserted relationship with them is also an error. A confidence score on a human assertion is meaningless, and an inference without one is unauditable.
The three assurance levels
| Level | Meaning | Carries confidence |
|---|---|---|
attested | Backed by a verified content credential — a C2PA manifest, a signed device output | No |
asserted | Declared by a system of record, or confirmed by a human | No |
inferred | Proposed rather than declared, carrying its score and method identifier | Yes, required |
These three appear in the same form wherever assurance is expressed — on relationships here, and on assets. One vocabulary, one rule about confidence.
An inferred edge, end to end
This is the full lifecycle of a machine proposal, which is what makes federation safe rather than merely automatic.
1. A connector proposes. It has found a plausible match between an asset in the graph and one in an external system, and scores it by whatever method it used:
{
"id": "rel_0042",
"relation": "same_as",
"from": { "kind": "asset", "id": "plate_0140" },
"to": { "kind": "asset", "id": "ext_frameio_b2c8e9f1" },
"assurance": "inferred",
"confidence": 0.74,
"method": "m-probabilistic-1",
"proposed_by": "connector_frameio",
"proposed_at": "2026-08-20T09:14:00Z"
}2. An acceptance policy decides what happens next. What confidence warrants automatic acceptance, what warrants review, and what is discarded outright is entirely an implementation decision — the specification takes no position, because the right policy depends on the content, the systems involved, and the consequences of a wrong match.
What the specification does require is that whatever happens next is recorded.
3. A human confirms. Acceptance does not overwrite the proposal — it annotates it:
{
"id": "rel_0042",
"relation": "same_as",
"from": { "kind": "asset", "id": "plate_0140" },
"to": { "kind": "asset", "id": "ext_frameio_b2c8e9f1" },
"assurance": "inferred",
"confidence": 0.74,
"method": "m-probabilistic-1",
"proposed_by": "connector_frameio",
"proposed_at": "2026-08-20T09:14:00Z",
"accepted_by": "s.okafor",
"accepted_at": "2026-08-20T11:03:00Z"
}Note what is preserved. The edge stays inferred with its original score and method, and gains an acceptance. It does not get promoted to asserted, because that would erase the fact that a machine proposed it at 0.74 confidence and a named person waved it through. Six months later, when the match turns out to be wrong, the question "who accepted this and on what evidence" has an answer.
This is the graph's own provenance rules applied to the graph's own construction — the claim is only meaningful if the acceptance is recorded, which is why accepted_by exists.
4. Acceptance may be automatic. Where an implementation's policy accepts a proposal without human review, the accepting actor is still named:
{
"id": "rel_0043",
"relation": "same_as",
"from": { "kind": "asset", "id": "plate_0141" },
"to": { "kind": "asset", "id": "ext_frameio_c9d1f0a2" },
"assurance": "inferred",
"confidence": 1.0,
"method": "m-exact-identifier",
"proposed_by": "connector_frameio",
"proposed_at": "2026-08-20T09:14:00Z",
"accepted_by": "connector_frameio",
"accepted_at": "2026-08-20T09:14:00Z"
}The connector accepted its own proposal, and says so. An auto-accept is still an accept, with an actor.
The method field
method is an opaque identifier naming which method produced the inference. The specification requires that it be present and stable; it does not define a vocabulary, and an implementation's methods are its own.
The reason it is required at all is auditability rather than description: when a class of match later proves unreliable, you need to select every edge produced by that method and re-review the set. That is only possible if the method is recorded as a stable, filterable value.
Two rules follow:
- Keep method identifiers stable. Renaming one orphans every edge produced under the old name.
- Make them specific enough to be useful as a filter. A single value used for every inference tells a future reviewer nothing.
Confidence is not accuracy.
confidencestates how strongly that method believes the match, in its own terms. Two different methods' scores are not directly comparable — which is exactly whymethodis required alongside, and why acceptance policy belongs to the implementation that knows what its own scores mean.
Relation vocabulary
An open set — the specification does not constrain relation, because domains will need predicates it has not anticipated. Documented values:
| Relation | Meaning |
|---|---|
same_as | The two endpoints are the same real thing in different systems |
derived_from | The from endpoint originates in the to endpoint |
version_of | Endpoints are versions of one lineage. Prefer asset predecessor where it applies |
depicts | The asset shows the referenced subject |
features | The asset features the referenced participant or character |
part_of | Structural containment |
references | A weaker link than derived_from — cites without deriving |
Direction matters and is easy to get backwards. For derived_from, from is the newer thing and to is the source: the edge reads from derived_from to. Every relation reads in that order — subject, predicate, object. Asserting the reverse silently inverts lineage answers, so it is worth a test.
Where semantic edges come from
An edge like features requires understanding the content. In OWG, whatever produced that understanding is itself a task — with an executor, a participant, and a cost:
{
"tasks": [
{
"id": "tag_faces",
"type": "work",
"label": "Identify featured performers",
"executor": { "type": "agent" },
"ai_role": "assistive",
"performed_by": "agent_vision",
"used": ["master_mp4"],
"produced": ["face_tags"],
"failure_mode": "CONTINUE"
}
],
"relationships": [
{
"id": "rel_0100",
"relation": "features",
"from": { "kind": "asset", "id": "master_mp4" },
"to": { "kind": "participant", "id": "perf_lead" },
"assurance": "inferred",
"confidence": 0.88,
"method": "m-subject-recognition",
"proposed_by": "agent_vision",
"proposed_at": "2026-08-20T14:02:00Z"
}
]
}So an inferred relationship carries the same provenance as any other piece of work: proposed_by names the agent, and the agent's own operated_by chain names the human accountable for it. Enrichment is not a special case that escapes accountability.
Guidance
- Do not mirror structural edges here. If a task's
used/producedalready states it, adding a relationship is duplication that will drift. - Always populate
proposed_by. An edge with no proposer cannot be reviewed, revoked as a class, or trusted. - Never round confidence to 1.0 unless the match is genuinely deterministic. Overstating confidence is the one agent failure mode that corrupts the graph rather than merely wasting a review.
- Keep
methodstable. It is a filter key. Renaming a method orphans every edge produced under the old name.