1. Summary
Extend Pass 2 of commit_functions::commit() so that when a declared relationship is persisted as a SmartLink, the commit logic also resolves and persists the corresponding inverse SmartLink automatically.
This enforces the MAP schema contract that all declared relationships are bidirectional and materialized, and ensures graph traversal works correctly from both directions.
2. Problem Statement
The MAP type system defines DeclaredRelationshipType and InverseRelationshipType as a paired schema contract:
- A declared relationship links to its inverse via
HasInverse
- The inverse links back via
InverseOf
- This guarantees bidirectional traversal of relationships
However, Pass 2 of commit_functions::commit() currently:
- Persists only forward
SmartLinks from the declaring side
- Does not resolve or persist inverse relationships
- Does not enforce the schema requirement that inverses must exist
As a result:
- Graph traversal from the target side may fail silently
- Schema guarantees are not enforced at persistence time
- Relationship consistency depends on caller behavior (which is incorrect)
Importantly:
Inverse relationships are not directly writable via the MAP API
Therefore:
- Callers cannot and should not stage inverse relationships
- Commit-time population is the only valid mechanism for materializing inverse links
- Missing inverses represent a schema contract violation
3. Scope
This issue focuses exclusively on:
- Resolving inverse relationship names from the type system
- Persisting inverse
SmartLinks during Pass 2
- Enforcing declaring-side-only mutation semantics
This issue does not include:
- Changes to the
StagedHolon state machine
- Relationship source anchoring refinements (see Issue 2)
- Duplicate detection or idempotency behavior (see Issue 3)
4. Proposed Solution
4.1 Commit-Side Inverse Resolution
For each declared relationship collection being committed:
- Determine the source
LocalId used for forward SmartLink persistence
- Follow
DescribedBy to the source holon’s TypeDescriptor
- Follow
InstanceRelationships to candidate DeclaredRelationshipType descriptors
- Match the relationship name against the descriptor’s
type_name
- Follow
HasInverse to the corresponding InverseRelationshipType
- Read the inverse descriptor’s
type_name to determine the inverse relationship name
- For each forward relationship member:
- Persist the forward
SmartLink
- Persist the inverse
SmartLink with endpoints reversed
4.2 Declaring-Side-Only Mutation Rule
- Relationship mutations originate only from the declaring (forward) side
- Inverse relationships are never staged or directly written by callers
- Commit is responsible for inverse materialization
4.3 Source Selection (Current Behavior)
This issue reuses the existing logic for determining the source LocalId of forward SmartLinks.
⚠️ Note:
- This behavior will be refined in Issue 2, which introduces explicit staged states (
ForUpdateNewVersion, ForUpdateGraphOnly) and a formal relationship anchoring rule
- When Issue 2 lands, both forward and inverse persistence must derive their source from that rule rather than assuming the staged holon is always the source
4.4 Bootstrap Safety
Inverse resolution must work when schema holons are:
- staged (during bootstrap), or
- already persisted
Traversal should:
- read staged relationship maps when available
- fall back to persisted graph traversal otherwise
The invariant is:
Schema closure must be resolvable from staged or saved state
4.5 Schema Invariant
All DeclaredRelationshipType descriptors are expected to have an inverse.
If:
- a declared relationship is matched, and
- no
HasInverse target can be resolved
then:
- this must be treated as a schema error, not silently ignored
4.6 External Target Constraint
Inverse persistence is only valid when the target holon is local.
If a relationship target resolves to a non-local holon:
- forward
SmartLink may still be written (if supported by current logic)
- inverse
SmartLink must not be written locally
This defines a boundary of schema contract enforcement.
5. Scope and Impact
| Component |
File(s) |
Impact |
| Commit Pass 2 |
commit_functions.rs |
Primary implementation site |
| SmartLink persistence |
smartlink_adapter.rs |
Likely unchanged |
| Type graph traversal |
descriptor resolution logic |
Extended for inverse lookup |
Out of scope:
- StagedHolon state model changes (Issue 2)
- Duplicate detection and idempotency (Issue 3)
- Inverse deletion semantics
- Cardinality enforcement
- External/proxied inverse propagation
6. Testing Considerations
- Verify forward + inverse traversal for committed relationships
- Verify inverse resolution works with staged schema (bootstrap)
- Verify inverse resolution works with persisted schema
- Verify missing
HasInverse produces a schema error
- Verify external targets do not cause commit failure
7. Definition of Done
8. Key Design Principles
- Inverse relationships are schema-enforced, not caller-supplied
- All mutations originate from the declaring side
- Commit enforces schema contracts, not just mechanical persistence
- Inverse persistence must mirror forward persistence using the same resolved source
1. Summary
Extend Pass 2 of
commit_functions::commit()so that when a declared relationship is persisted as aSmartLink, the commit logic also resolves and persists the corresponding inverseSmartLinkautomatically.This enforces the MAP schema contract that all declared relationships are bidirectional and materialized, and ensures graph traversal works correctly from both directions.
2. Problem Statement
The MAP type system defines
DeclaredRelationshipTypeandInverseRelationshipTypeas a paired schema contract:HasInverseInverseOfHowever, Pass 2 of
commit_functions::commit()currently:SmartLinks from the declaring sideAs a result:
Importantly:
Therefore:
3. Scope
This issue focuses exclusively on:
SmartLinks during Pass 2This issue does not include:
StagedHolonstate machine4. Proposed Solution
4.1 Commit-Side Inverse Resolution
For each declared relationship collection being committed:
LocalIdused for forwardSmartLinkpersistenceDescribedByto the source holon’sTypeDescriptorInstanceRelationshipsto candidateDeclaredRelationshipTypedescriptorstype_nameHasInverseto the correspondingInverseRelationshipTypetype_nameto determine the inverse relationship nameSmartLinkSmartLinkwith endpoints reversed4.2 Declaring-Side-Only Mutation Rule
4.3 Source Selection (Current Behavior)
This issue reuses the existing logic for determining the source
LocalIdof forwardSmartLinks.ForUpdateNewVersion,ForUpdateGraphOnly) and a formal relationship anchoring rule4.4 Bootstrap Safety
Inverse resolution must work when schema holons are:
Traversal should:
The invariant is:
4.5 Schema Invariant
All
DeclaredRelationshipTypedescriptors are expected to have an inverse.If:
HasInversetarget can be resolvedthen:
4.6 External Target Constraint
Inverse persistence is only valid when the target holon is local.
If a relationship target resolves to a non-local holon:
SmartLinkmay still be written (if supported by current logic)SmartLinkmust not be written locallyThis defines a boundary of schema contract enforcement.
5. Scope and Impact
commit_functions.rssmartlink_adapter.rsOut of scope:
6. Testing Considerations
HasInverseproduces a schema error7. Definition of Done
SmartLinks at commitHasInverseis treated as a schema error8. Key Design Principles