A document is a selected part of a graph
This looks like a tree:
name: Weekend Package Agreement
status: assembling
orders:
flowers:
type: Flower Order
status: proposed
dinner:
type: Dinner Order
status: proposed
payment:
blueId: 9Q1jK...
It isn't. It's a selected slice of a graph. Some nodes are written inline. Some — like payment — are edges pointing to nodes that live elsewhere. The type fields are edges too, pointing at documents that define what Flower Order means. Those documents point at further documents, and so on outward.
A Blue document is whatever you selected: one root, plus as much of the reachable graph as you chose to materialize. Three consequences:
- A document doesn't need to contain everything it references.
blueId: 9Q1jK...is not a broken link; it's an exact edge. Content gets fetched when an operation actually needs it. - The root defines the document. There is no wrapper above it. An Agreement, an Order, and an Invoice can all reference the same payment terms node — they remain three documents, because they have three roots.
- Materializing or collapsing a reference changes nothing. Inline and referenced are two spellings of the same edge.
Any document can be a type
Blue has no schema-versus-instance divide. A document describing an order:
name: Order
orderId:
type: Text
status:
type: Text
can be used as a type by a more specific document:
name: Dinner Order
type: Order
currency: PLN
status: proposed
restaurant:
type: Restaurant
amountMinor:
type: Integer
schema:
minimum: 0
which can itself be a type for a concrete order:
name: Alice's Friday Dinner
type: Dinner Order
orderId: dinner-204
amountMinor: 38000
restaurant:
name: Old Town Restaurant
A type is an overlay source: it contributes structure, constraints, fixed values, and behavior. The child adds to it. There is no separate category of "schema" — just documents used to give other documents meaning.
Extension adds; it never contradicts
If a parent fixes a value, that value is a fact, not a default. Dinner Order fixed currency: PLN, so this is illegal:
# Invalid — the parent fixed PLN
name: Euro Dinner Order
type: Dinner Order
currency: EUR
The test is the familiar Liskov principle: anywhere a parent is accepted, the child must remain valid.
| Parent guarantees | Child may | Child may not |
|---|---|---|
| A fixed value | preserve it | replace or contradict it |
| A field | keep it, add others | remove it |
| A constraint | repeat or narrow it | widen it |
A service that reads orderId and status from any Order works, unchanged, on every Dinner Order ever defined — including ones defined years later by people the service authors never met. That is the entire point.
Resolution: making inherited meaning explicit
Authors write compact overlays. Machines sometimes need the full picture. Resolution walks the type chain and merges everything inherited into an explicit, validated form.
Before — what the author wrote:
name: Alice's Friday Dinner
type: Dinner Order
amountMinor: 38000
restaurant:
name: Old Town Restaurant
After — what the document means:
name: Alice's Friday Dinner
type: Dinner Order
currency: PLN # inherited fact
status: proposed # inherited fact
amountMinor:
type: Integer
schema:
minimum: 0 # inherited constraint — now checked against 38000
value: 38000
restaurant:
type: Restaurant
name: Old Town Restaurant
Nothing was invented; everything implicit became explicit, and every inherited guarantee was verified against what the instance supplied. Resolution can also be partial — a processor touching /status doesn't have to resolve an unrelated catalog just because the document can reach one.
Minimization: the inverse
Minimization strips everything recoverable from the type chain, keeping only what the instance genuinely contributes — which is exactly the "before" form above. Two different minimized forms can resolve to the same meaning; minimization is for authors, and authors have taste. Identity is a separate question, with exactly one answer — that's the next section.
repo.blue — and what happens to type names
Shared types need a shared home. repo.blue publishes reusable definitions — Timeline Entry, Operation Request, Operation Mandate, and the rest — each with a human name and an exact identity.
One thing to understand about those names: type: Dinner Order is authoring convenience, not identity. During preprocessing — before anything is resolved or hashed — every name in a type position is replaced by the exact reference it is bound to:
# what you write
type: Dinner Order
# what the document actually contains afterward
type:
blueId: 9Q1jK...
The name never survives into the document's identity. Two authors using different aliases — even different natural languages — for the same definition produce literally the same document. repo.blue is a dictionary, not a database: it translates names into exact meanings, and what the document says is the meaning, never the name.