Open Workflow GraphPre-release
Specification

Relationships and assurance

Explicit edges, the three assurance levels, and exactly where a machine proposal's confidence score and method live in the payload.

View as markdownMachine-readable source for agents and scripted implementers

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

StructuralExplicit
WhereImplicit in tasks[]used, produced, performed_by, ran_on, depends_onThe relationships[] array
CostFree. A by-product of describing workRequires intelligence or a human
AssuranceAlways asserted by the executing systemAny of three levels
ExamplesThis task consumed that plateThis 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"
    }
  ]
}
FieldTypeNotes
idstringRequired. Identifies the edge, so a decision about it can be recorded
relationstringRequired. The predicate. See vocabulary
fromobjectRequired. { kind, id }
toobjectRequired. { kind, id }
assuranceenumRequired. attested | asserted | inferred
confidencenumber0.0–1.0. Required when assurance is inferred; forbidden otherwise
methodstringRequired when assurance is inferred; forbidden otherwise
proposed_bystringParticipant id — the connector, agent, or person proposing
proposed_atstringRFC 3339 timestamp
accepted_bystringParticipant id of whoever confirmed it. Absent while pending
accepted_atstringRFC 3339 timestamp of acceptance
metadataobjectFreeform
profilesobjectProfile-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

LevelMeaningCarries confidence
attestedBacked by a verified content credential — a C2PA manifest, a signed device outputNo
assertedDeclared by a system of record, or confirmed by a humanNo
inferredProposed rather than declared, carrying its score and method identifierYes, 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. confidence states how strongly that method believes the match, in its own terms. Two different methods' scores are not directly comparable — which is exactly why method is 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:

RelationMeaning
same_asThe two endpoints are the same real thing in different systems
derived_fromThe from endpoint originates in the to endpoint
version_ofEndpoints are versions of one lineage. Prefer asset predecessor where it applies
depictsThe asset shows the referenced subject
featuresThe asset features the referenced participant or character
part_ofStructural containment
referencesA 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/produced already 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 method stable. It is a filter key. Renaming a method orphans every edge produced under the old name.