N-ary Event Objects
Alice introduced Bob to Carol
Aa
A sign is something, A, which brings something, B, its interpretant sign determined or created by it, into the same sort of correspondence with something, C, its object, as that in which itself stands to C.
A31 preserves event identity where role bindings, atomicity, or cross-role constraints would otherwise be lost. The claim is conditional: an explicit event object is needed only when the alternative encoding carries no equivalent grouping information.
The Sentence That Won't Flatten
"Alice introduced Bob to Carol at the conference."
One sentence. Three people. One event. Try to store it in a system that only knows binary relations.
First attempt: two edges.
(Alice, introduced, Bob)
(Alice, introduced_to, Carol)
This preserves something. Alice is the subject of both edges; she must be the introducer. Bob appears with "introduced"; Carol with "introduced_to." The structure seems to encode the sentence.
Now compare two possible collections of introductions at the conference:
Collection A:
Alice introduced Bob to Carol
Alice introduced Dave to Eve
Collection B:
Alice introduced Bob to Eve
Alice introduced Dave to Carol
Project each event into the two role edges above, retaining only their union. Both collections produce exactly the same graph:
(Alice, introduced, Bob)
(Alice, introduced, Dave)
(Alice, introduced_to, Carol)
(Alice, introduced_to, Eve)
Yet “whom did Alice introduce to Carol?” has different answers: Bob in collection A, Dave in collection B. No query over this stored graph can recover which collection supplied it. Returning both would invent an introduction in either history.
This is a constructed example of information loss. The role names still tell us who introduces and who receives an introduction; what has disappeared is the pairing between those roles in each occurrence. If the record also needs to support a correction to one occurrence, the lost grouping matters again.
T10 concerns this loss of grouping. The original sentences distinguish the introductions; their projected record no longer does. Restoring the role pairing would recover an answer that no further search of that record can supply.
A31 supplies one explicit representation that preserves the event grouping.
Events as Objects
The projection has omitted the information that binds role fillers to an occurrence. An event identifier can retain that grouping. A tuple, a statement identifier or another recoverable representation may retain it too. Retraction requires enough information to select the intended occurrence; a name is one convenient way to supply it.
One repair is to represent events as objects1. An event is like a database row with a primary key, or a function call frame with named arguments: a single identity that binds its parts. A31 adopts that modeling choice so later mutations and retractions can address the grouping directly.
An edge here connects two entities: (subject, predicate, object). The unkeyed projection records the edges without their event grouping.
An event binds multiple entities into typed roles under a single identity. In this proposed object model, an event is the unit whose role bindings must be created, deleted and constrained together. The implementation must enforce those operations.
The introduction sentence is not two facts about Alice. It is one event with three roles:
| Role | Filler |
|---|---|
| introducer | Alice |
| introduced | Bob |
| to | Carol |
The event identifier lets a deletion select all its role bindings. A31 requires their atomic deletion and per-event checking of the constraint “introducer ≠ introduced.” Both are obligations for the implementation.
Anchor A31: N-ary Event Object
RoleSpec defines a role slot:
RoleSpec := { type: EntityType, cardinality: single | multi }
EventType defines the schema:
EventType := {
name: TypeName,
role_schema: { role_name: RoleSpec }...,
constraints: [Constraint...],
context_requirements: ContextSpec
}
Event is an instance:
Event := {
id: EventID, // the event_id (join key)
type: EventType,
roles: { role_name: Entity | Collection[Entity] }...,
context: Context,
witness: EventWitness
}
Role projection: for a single-valued role; a multi-valued role projects to its declared collection of entities.
For each role in type.role_schema, .
Invariants:
- COMPLETENESS: All roles must be filled.
- TYPE_CONFORMANCE: Each filler matches its role's declared type ().
- ROLE_FUNCTIONALITY: Each role is single-valued unless declared multi.
- CONSTRAINT_SATISFACTION: All type constraints hold at instantiation.
- CONTEXT_BINDING: Event is anchored in declared context.
The introduction event:
EventType: Introduction = {
name: "Introduction",
role_schema: {
introducer: { type: Person, cardinality: single },
introduced: { type: Person, cardinality: single },
to: { type: Person, cardinality: single }
},
constraints: [
introducer ≠ introduced,
introduced ≠ to,
introducer ≠ to
],
context_requirements: { has_location: true, has_time: true }
}
Event: e1 = {
id: "intro_001",
type: Introduction,
roles: {
introducer: Alice,
introduced: Bob,
to: Carol
},
context: conference_2024,
witness: ref(W-7234)
}
Projections:
Constraints checked at creation:
- Alice ≠ Bob ✓
- Bob ≠ Carol ✓
- Alice ≠ Carol ✓
The proposed creation must commit the complete, checked event or leave it uncreated. It must not expose orphan role bindings as a completed event.
What Binarization Loses
Binarization: An encoding that introduces no fresh event_id (join key) beyond the domain entities. No new identifier—node, edge label, or synthetic "pairing" relation—may serve as a stable grouping key across mutations.
Remark: Blank nodes, statement IDs, named graphs, quads or RDF* can carry the required grouping. Where they do, they fall outside this deliberately restricted encoding. Their mere presence does not establish that each required occurrence can be recovered.
We care about encodings that support (i) querying role-bound events, (ii) atomic create/delete, and (iii) per-instance constraint checks.
Let and be distinct finite collections of role-bound events, and let be an encoding with . Suppose a required query distinguishes the collections: .
Then no function of the encoded record alone can answer correctly for both collections. If did so, the equality would give , contradicting the required unequal answers.
The opening supplies such a collision for the union of two role projections. The same information requirement applies when a required mutation or integrity check distinguishes collections the encoding has identified. A representation supporting those operations must retain the relevant grouping distinctions. This result does not rule out binary encodings, tuple keys or other structures that preserve them.
The three operations ask different things of a representation, but each may need the grouping that this projection discards. Restoring that information makes the operations expressible. It does not execute a transaction or enforce a constraint by itself.
Loss 1: Instance Binding
The two collections in the opening have identical projected edges and different answers. Their shared participant is not by itself the problem; the collision is. An encoding that distinguishes the collections can answer the query.
One fix is to add event identifiers:
(e1, introduced, Bob)
(e1, introduced_to, Carol)
(e2, introduced, Dave)
(e2, introduced_to, Eve)
The event identifiers now preserve which role edges belong together. This binary representation supplies the grouping that the unkeyed projection lost. Its remaining obligations concern the declared role constraints and the behavior of updates.
Loss 2: Atomicity
For A31’s event identifier, the implementation must provide these atomic operations:
CreateEvent(e1, ...)creates all role bindings together.DeleteEvent(e1)deletes all role bindings together.- No orphan bindings. No partial events.
In binarization without event identifiers:
DELETE (Alice, introduced, Bob)
Does this delete event e1? Or just one "fact"? If we delete one edge, others remain:
(Alice, introduced_to, Carol) // orphan
We now have "Alice introduced someone to Carol" with no "introduced" binding. The event is fragmented.
To restore atomicity, we need transaction grouping: "Delete all edges that belong to event e1." But this requires knowing which edges belong to e1. Which requires recoverable grouping information.
A database transaction can supply atomicity once the intended changes are identified. It cannot recover the lost pairing in the opening graph. A join key, a tuple or another adequate selector can supply the missing grouping; the transaction and the representation then perform different parts of the work.
Loss 3: Constraint Scope
The constraint "introducer ≠ introduced ≠ to" must be enforced per-event-instance.
In A31: the constraint is checked at event creation time, on the event object's role bindings.
In binarization, how do we enforce this?
Attempt 1: Per-edge constraint
(Alice, introduced, Alice) // reject: subject = object
This catches Alice introducing herself. A similar per-edge rule on introduced_to catches Alice appearing as both introducer and recipient. Neither needs a cross-edge comparison.
The remaining condition—introduced person distinct from recipient—does require the pairing:
(Alice, introduced, Bob)
(Alice, introduced_to, Bob)
Those edges violate the condition if they belong to one event. They need not do so if Alice introduced Bob to Carol and Dave to Bob in two different events. Checking every pair of edges sharing Alice would reject the latter collection incorrectly. The validator needs the actual role groups, whether carried by event objects or an equivalent representation.
When an Event Identifier Is Needed
Within the proposition's scope, a lossless encoding must introduce an event identifier or information equivalent to one. Once made explicit, the representation can provide:
- Event node + role projections (A31's structure)
- With typed roles (A31's role_schema)
- With constraint enforcement (A31's constraints)
A31 packages those requirements as an event object. Other encodings may preserve the same grouping information without using this particular schema.
The Recommendation Event
T10's running example: "User A recommended Dress X to User B during the trunk show."
Naive binarization:
(UserA, recommended, DressX)
(DressX, recommended_to, UserB)
The edge labels identify recommender and recipient in this single example. Repeated recommendations of the same item can still lose their pairing: UserA to UserB and UserC to UserD yield the same projected edges as UserA to UserD and UserC to UserB. An occasion or later correction also requires the representation to preserve the occurrence to which it belongs.
A31 representation:
EventType: ProductRecommendation = {
name: "ProductRecommendation",
role_schema: {
recommender: { type: User, cardinality: single },
item: { type: Product, cardinality: single },
recipient: { type: User, cardinality: single },
occasion: { type: EventContext, cardinality: single }
},
constraints: [
recommender ≠ recipient,
item.available_in(context)
],
context_requirements: { has_time: true }
}
Event: rec_001 = {
id: "rec_001",
type: ProductRecommendation,
roles: {
recommender: UserA,
item: DressX,
recipient: UserB,
occasion: trunk_show_2024
},
context: { time: "2024-03-15T14:30:00Z", channel: "in_person" },
witness: ref(W-8891)
}
Query by role:
"Who recommended DressX?"
→ π_recommender(rec_001) = UserA
"What was recommended to UserB at trunk shows?"
→ Filter events: π_recipient = UserB AND occasion.type = trunk_show
→ Return π_item for each match
→ Result: [DressX, DressY, ...]
Enforce constraints:
Attempted: UserA recommends DressX to UserA
→ REJECTED: recommender ≠ recipient violated
→ ConstraintViolation { event_type: ProductRecommendation,
constraint: "recommender ≠ recipient",
values: { recommender: UserA, recipient: UserA } }
Preserve event identity:
Delete recommendation rec_001:
→ All role bindings deleted atomically
→ No orphan edge "DressX recommended_to UserB" remains
→ EventDeletionReceipt { id: rec_001, roles_deleted: 4 }
The proposed operations make role queries and occurrence-specific corrections explicit. Their guarantees depend on validating every write path and performing the grouped changes atomically, as the implementation comparison below requires.
Integration with Context Graph
An event identifier can remain a host entity token under A22. The substrate can represent its type and role bindings as a bundle of Claims, link their evidence through supports, their assertion context through scoped_to, and the relevant role requirements as Constraints. This introduces no sixth A22 node kind.
The host event’s occurrence time and place are content of those claims. They are not identical to the context in which a source asserts them. A later attestation about an earlier event must preserve both.
A schematic CreateEvent operation checks completeness, role types, cardinalities, cross-role constraints and the required occurrence information. On success it stores the bundle and its evidence under the same event identifier. Missing grounds or an unfinished required check must remain distinct from a proved violation.
Deletion or amendment requires a declared atomic update boundary so that the representation does not leave orphan role bindings or expose a prohibited partial state. A claim bundle alone does not enforce that boundary. The implementation must supply it, while retaining the correction evidence required by the governing access and retention rules. Deleting the representation does not undo the event it describes.
Event Evolution
Event types evolve (A26 integration):
Role addition (compatible when the old contract is preserved):
ProductRecommendation_v2 adds:
{ recommendation_reason: ReasonCode? }
Migration:
- Existing events get recommendation_reason = unknown
- CompatibilityWitness(conservative, nullable_addition)
Role removal (breaking):
Removing a role breaks a contract that still requires reading it. For such consumers, migration must preserve or explicitly retire that access:
Breaking change manifest:
- Affected events: [rec_001, rec_002, ...]
- Migration plan: archive role values, then remove
- Compatibility: v2 cannot read v1 events without migration
Constraint tightening (breaking):
Adding “recommender.reputation > 3” can exclude existing events. A claim that all of them satisfy the revised type requires checking that additional condition over the declared population.
Constraint relaxation (existing instances remain valid):
Removing a constraint leaves previously valid events valid. It can admit new events and withdraw a guarantee on which a query relied. That is different from deductive conservativity, which preserves exactly the old-language consequences.
The versioning discipline from A26 applies to the contract actually promised: record which instances remain readable, which queries retain their meaning, and which guarantees require migration.
Reification Is Not Enough
RDF can preserve an n-ary relation by introducing a relation instance and linking its participants; the W3C n-ary relation pattern documents this construction. SHACL can validate role types, cardinalities, and cross-property conditions and return structured violation reports.
Those mechanisms are relevant implementations of parts of the requirement. Validation alone does not establish transaction atomicity, mandatory checking on every write path, or a versioning policy. An implementation must supply and demonstrate those properties. The same burden applies to an implementation of A31: putting invariants in an event-object specification does not enforce them by itself.
A31 packages stable event grouping, typed roles, and associated constraints in one proposed object model. An enriched graph or relational encoding may satisfy the same contract. Calling its components "tooling" or describing them as a reconstruction of A31 does not establish that their guarantees are weaker; that requires a comparison of the actual enforcement boundary and evidence.
Where instance binding, atomic deletion, and cross-role constraints matter, a representation must retain the grouping information that identifies which roles belong to one event. An explicit event object is one direct way to do so.
For the examples in T10, naive binary edges lose that grouping and A31 preserves it. The proposition does not rule out enriched binary encodings that carry equivalent identifiers or constraints.
The useful saving is to retain the grouping once, so that later operations can check which roles changed without reconstructing the event from an ambiguous union of edges.