MAP Rust-API Developer Guide¶
The reference layer is the boundary between your application code and the underlying holon store and services. It provides:
- Uniform handles (“references”) to holons, regardless of whether they are transient, staged, or cached/saved.
- A small, consistent read/write API that hides internal manager and phase differences.
- High-level operations for staging, committing, and deleting.
- A curated prelude for ergonomic imports and a stable public API.
This guide explains the API surface and shows how to perform the most common tasks.
1. Prelude¶
To simplify imports, use the MAP prelude:
use holons_prelude::prelude::*;
This brings into scope:
- Core value and identifier types (
BaseValue,MapString,HolonId) - Reference traits and types (
HolonReference,ReadableHolon,WritableHolon,TransientReference,StagedReference,SmartReference) - Context traits (
HolonsContextBehavior) - Common operations (
stage_new_holon_api,commit_api, etc.) - Type-name helpers (
CorePropertyTypeName,CoreRelationshipTypeName,ToPropertyName,ToRelationshipName) - Dance protocol builders (
holon_dance_builders::*) - Query types (
Node,NodeCollection,QueryExpression)
Use
holons_prelude::prelude::v1::*if you need to pin to a stable prelude version.
2. Context: your execution environment¶
Every holon operation in MAP runs within a context that implements the HolonsContextBehavior trait. This context is the execution environment for all read, write, and commit actions—it provides access to the active workspace where your holons live.
As a developer, you don’t create or manage the context yourself. Your mApp receives it from the runtime whenever you perform operations that touch holons.
You simply pass it along to each API call that needs it.
Example — staging and committing a holon:
fn create_and_commit(
context: &dyn HolonsContextBehavior,
) -> Result<(), HolonError> {
// Create a new transient holon of a given type
let transient = create_empty_transient_holon(context, MapString("ExampleType".into()));
transient.with_property_value(context, P::Key, "example-1".into())?;
// Stage it for persistence
let staged = stage_new_holon_api(context, transient)?;
// Commit all staged changes
let response = commit_api(context)?;
println!("Committed {} holons.", response.commits_attempted);
Ok(())
}
Key points for developers¶
-
The context is always the first parameter to any read or write call.
It carries all the state needed to resolve references, access relationships, and enforce permissions. -
You can think of it as your “session” or “workspace.”
Everything that happens during a transaction—reads, writes, staging, commits—occurs within this context. -
The context automatically routes requests to the right implementation based on the holon’s phase (transient, staged, or saved).
You never need to know which storage or cache layer is involved. -
Contexts are lightweight and short-lived.
They are passed around as immutable references (&dyn HolonsContextBehavior), so you can safely use them in async or concurrent flows.
In short: the context is your gateway to the MAP.
You use it everywhere, but you don’t manage or configure it—just pass it through to the reference-layer API.
3. Reference Kinds¶
HolonReference is the umbrella type that can represent any phase:
TransientReference— in-memory, mutable, not persisted.StagedReference— managed and ready to commit.SmartReference— read-only, backed by a saved holon in cache or storage.
All three implement the ReadableHolon trait; transient and staged also implement WritableHolon.
| Type | Backing store | Read | Write | Commit |
|---|---|---|---|---|
TransientReference |
In-memory | ✅ | ✅ | ❌ (must stage) |
StagedReference |
Staging area | ✅ | ✅ | ✅ |
SmartReference |
Saved/cache | ✅ | ❌ | ❌ |
4. Collections¶
A HolonCollection represents an ordered set of holon references. Collections are most commonly returned when traversing relationships—especially many-to-many or one-to-many links between holons. For example, a Project holon might have a HasTask relationship that returns a HolonCollection of Task holons.
Example: Reading related holons¶
let tasks = project_ref.related_holons(context, R::HasTask.as_relationship_name())?;
for task_ref in tasks.iter() {
let title = task_ref.property_value(context, &P::Title)?;
println!("Task: {:?}", title);
}
Key capabilities¶
1. Access and iteration¶
HolonCollection behaves like a lightweight, reference-aware vector:
let count = tasks.len();
let first = tasks.first();
let by_index = tasks.get(2);
let all = tasks.to_vec(); // Clones references into a Vec<HolonReference>
You can iterate directly:
for holon_ref in &tasks {
println!("{:?}", holon_ref.key(context)?);
}
2. Membership and lookup¶
if tasks.contains(&some_ref) {
println!("Already linked!");
}
If the collection is keyed (e.g., by MapString), you can look up by key:
if let Some(found) = tasks.get_by_key(&MapString("task-42".into())) {
println!("Found task 42: {:?}", found.key(context)?);
}
3. Mutation (transient and staged only)¶
Collections tied to writable holons can be updated via the HolonCollectionApi trait.
These operations automatically update both the in-memory collection and the relationship map on the parent holon.
// Add a new related holon
tasks.add(context, HolonReference::Transient(new_task_ref))?;
// Remove an existing related holon
tasks.remove(context, HolonReference::Smart(old_task_ref))?;
Behind the scenes, this uses the same logic as calling:
parent_ref.add_related_holons(context, R::HasTask.as_relationship_name(), vec![child_ref])?;
4. Access control and phase safety¶
- Read-only collections (from
SmartReference) cannot be mutated. - Transient and staged collections support add/remove operations.
- Attempting to mutate a read-only collection returns
HolonError::NotAccessible.
5. Use cases¶
- Navigation: traverse relationships, fetch related holons, inspect their properties.
- Editing: attach or detach related holons while editing staged or transient instances.
- Synchronization: check if a relationship set has changed before committing.
Summary¶
| Operation | Available on | Description |
|---|---|---|
iter() |
All | Iterate through member references |
len() / is_empty() |
All | Get collection size |
get(index) / get_by_key() |
All | Access by position or key |
add() / remove() |
Transient, Staged | Modify collection membership |
contains() |
All | Check membership |
to_vec() |
All | Convert to owned vector |
In short:
HolonCollection gives you a simple, phase-safe way to traverse and manipulate sets of related holons, just like a Rust Vec, but with built-in access control and automatic relationship synchronization.
5. Read API (all reference types)¶
Every HolonReference implements ReadableHolon. That means the same read calls work on TransientReference, StagedReference, and SmartReference alike. Reads are phase-safe and route through the active HolonsContextBehavior.
Core calls¶
let key_opt = reference.key(context)?; // Option<MapString>
let vkey = reference.versioned_key(context)?; // Versioned key (type+key+rev/epoch)
let title_opt = reference.property_value(context, &P::Title)?; // Option<PropertyValue>
let children = reference.related_holons(context, R::HasChild.as_relationship_name())?; // HolonCollection
Other useful calls:
- all_related_holons(context) – returns a map of all relationship names → HolonCollection
- holon_id(context) – low-level persistent identifier (when applicable)
- predecessor(context) – previous version (if any)
- essential_content(context) – minimal content needed to reconstruct the holon
- into_model(context) – materialize a snapshot model (e.g., for serialization or UI presentation)
Ergonomic usage patterns¶
1) Keys and versioned keys¶
Use key() where present (some holons may be keyless) and fall back gracefully:
let label = reference
.key(context)?
.map(|k| format!("key: {}", k.0))
.unwrap_or_else(|| "keyless".to_string());
let vkey = reference.versioned_key(context)?; // Stable for logs, audit, and idempotency
2) Properties with defaults¶
property_value returns Option<PropertyValue>. Provide sensible defaults at the edge:
let title = reference
.property_value(context, &P::Title)?
.and_then(|v| v.as_string()) // Convert if your ValueType is String-like
.unwrap_or_else(|| "Untitled".to_string());
Tip: keep type conversion at the edge (e.g., UI layer). Treat raw
PropertyValueas data until you must render or compute.
3) Navigating relationships¶
Use relationship constants for clarity and refactor safety:
let children = reference.related_holons(context, R::HasChild.as_relationship_name())?;
for child in children.iter() {
let ck = child.key(context)?;
let ct = child.property_value(context, &P::Title)?.and_then(|v| v.as_string());
println!("child {:?} titled {:?}", ck, ct);
}
Fetch all relationships when you need to render a full card/graph:
let relmap = reference.all_related_holons(context)?;
for (rel_name, collection) in relmap.iter() {
println!("{}: {} linked holons", rel_name, collection.len());
}
4) Lineage-aware reads¶
When building edit screens for saved holons:
if let Some(prev) = reference.predecessor(context)? {
println!("has predecessor with key {:?}", prev.key(context)?);
}
5) Snapshotting for UI/persistence¶
Prefer into_model for immutable snapshots passed to views or serialization:
let model = reference.into_model(context)?;
// pass `model` to your presentation layer or serializer
Applicability (access rules)¶
- Read is allowed on all phases: Transient ✅, Staged ✅, Smart ✅.
- Access control is automatically enforced; if a read is not permitted you’ll get an error (see below).
- The same method set works across phases, so you can write one code path for all references.
You never need to detect the phase to read. Call the method; the context routes it correctly.
Error behavior you should expect here¶
All reads return Result<_, HolonError>. Common cases for this section:
HolonError::NotAccessible— read not permitted for the current state of this holonHolonError::InvalidHolonReference— reference cannot be resolved (e.g., missing, malformed, or out-of-scope).HolonError::InvalidType/HolonError::UnexpectedValueType— property exists but the value type doesn’t match your expectation.HolonError::EmptyField— property required by your logic is unset.
The guide’s Error Handling section enumerates all variants and recommended remedies. In read paths, prefer graceful fallbacks and edge validation (e.g., default titles, optional render elements).
Practical examples¶
Read a title with a default and render children count
fn render_card(
context: &dyn HolonsContextBehavior,
item: &HolonReference,
) -> Result<(), HolonError> {
let title = item.property_value(context, &P::Title)?
.and_then(|v| v.as_string())
.unwrap_or_else(|| "Untitled".to_string());
let kids = item.related_holons(context, R::HasChild.as_relationship_name())?;
println!("{} ({} items)", title, kids.len());
Ok(())
}
Collect all related holons for a relationship-aware widget
fn gather_relationships(
context: &dyn HolonsContextBehavior,
item: &HolonReference,
) -> Result<Vec<(RelationshipName, Vec<HolonReference>)>, HolonError> {
let mut out = Vec::new();
for (rel_name, coll) in item.all_related_holons(context)? {
out.push((rel_name.clone(), coll.to_vec()));
}
Ok(out)
}
Summary table¶
| Method | Returns | Notes |
|---|---|---|
key(context) |
Result<Option<MapString>, HolonError> |
Keyless holons return None. |
versioned_key(context) |
Result<VersionedKey, HolonError> |
Stable identifier for logs/audits. |
property_value(context, &P::X) |
Result<Option<PropertyValue>, HolonError> |
Convert at the edge (as_string(), etc.). |
related_holons(context, R::X) |
Result<HolonCollection, HolonError> |
Ordered, phase-safe collection. |
all_related_holons(context) |
Result<BTreeMap<RelationshipName, HolonCollection>, HolonError> |
Full relationship map. |
holon_id(context) |
Result<HolonId, HolonError> |
May be unavailable for purely transient holons. |
predecessor(context) |
Result<Option<HolonReference>, HolonError> |
Present on versioned/saved lineages. |
essential_content(context) |
Result<EssentialHolonContent, HolonError> |
Minimal reconstruction payload. |
into_model(context) |
Result<HolonNodeModel, HolonError> |
Immutable snapshot for UI/serialization. |
Takeaways - One trait, all phases: write once, read anywhere. - Treat properties as optional until rendered; fail soft at the edge. - Use relationship constants for clarity and stability.
6. Write API (transient/staged only)¶
Available via WritableHolon:
reference.with_property_value(context, P::Title, "Hello".into())?;
reference.remove_property_value(context, P::Obsolete)?;
reference.add_related_holons(context, R::HasChild.as_relationship_name(), vec![child_ref])?;
reference.remove_related_holons(context, R::HasChild.as_relationship_name(), vec![child_ref])?;
reference.with_descriptor(context, descriptor_ref)?;
reference.with_predecessor(context, Some(prev_ref))?;
7. Staging and Commit¶
High-level helpers in holon_operations_api:
- Stage a new holon
let staged = stage_new_holon_api(context, transient)?; - Stage a new version of a saved holon
let staged = stage_new_version(context, smart_ref)?; - Commit all staged changes
let commit_response = commit_api(context)?;
CommitResponse includes:
* saved_holons
* abandoned_holons
* commits_attempted
(saved + abandoned == attempted) indicates completion.
8. Cloning holons¶
MAP offers three public clone operations, depending on your intent.
A. ReadableHolon::clone_holon → TransientReference¶
fn clone_holon(
&self,
context: &dyn HolonsContextBehavior,
) -> Result<TransientReference, HolonError>
Any holon—saved, staged, or transient—can be cloned.
The result is always a new transient holon, detached from lineage or staging metadata but preserving its property and relationship data.
Use this for creating a scratch copy:
let transient_clone = any_reference.clone_holon(context)?;
transient_clone.with_property_value(context, P::Title, "Draft copy".into())?;
B. stage_new_version → StagedReference¶
fn stage_new_version(
context: &dyn HolonsContextBehavior,
current_version: SmartReference,
) -> Result<StagedReference, HolonError>
Creates a new staged holon as an update to an existing saved holon, retaining lineage through the Predecessor relationship.
let staged = stage_new_version(context, saved_ref)?;
staged.with_property_value(context, P::Status, "Revised".into())?;
C. stage_new_from_clone → StagedReference¶
fn stage_new_from_clone(
context: &dyn HolonsContextBehavior,
original_holon: HolonReference,
new_key: MapString,
) -> Result<StagedReference, HolonError>
Creates a new staged holon from any existing one, without lineage.
Ideal for templates or derivatives.
let staged_clone = stage_new_from_clone(context, original_ref, MapString("copy-123".into()))?;
| Operation | Input | Output | Lineage | Typical use |
|---|---|---|---|---|
clone_holon |
Any | TransientReference |
❌ | Scratch copy |
stage_new_version |
SmartReference |
StagedReference |
✅ | Update existing |
stage_new_from_clone |
Any | StagedReference |
❌ | Derive new holon |
9. Typical Flows¶
Create → stage → commit¶
let t = create_empty_transient_holon(context, MapString("MyType".into()));
t.with_property_value(context, P::Key, "example-1".into())?;
let staged = stage_new_holon_api(context, t)?;
let resp = commit_api(context)?;
Fetch saved holon → stage new version¶
let smart = HolonReference::from_id(saved_id);
let staged = stage_new_version(context, smart)?;
staged.with_property_value(context, P::Title, "updated".into())?;
let resp = commit_api(context)?;
Derive new staged holon from existing¶
let staged_clone = stage_new_from_clone(context, existing_ref, MapString("copy-2".into()))?;
10. Relationships¶
-
Read:
Smart holons fetch from cache (with fetch-on-miss).
Staged and transient holons use local relationship maps. -
Write:
Only staged or transient references can modify relationships.
Useadd_related_holons,remove_related_holons, orwith_descriptor.
11. Query Layer¶
Node, NodeCollection, and QueryExpression express queries over holons.
let expr = QueryExpression::property_equals(P::Title, "Hello".into());
let nodes: NodeCollection = run_query(context, expr)?;
for node in nodes {
println!("Found: {:?}", node.key(context)?);
}
Note: The query API is evolving. Current support includes basic property and relationship predicates.
12. Dance Builders¶
holon_dance_builders::* provides ergonomic constructors for standard Dances:
let request = build_commit_dance_request(staged_refs);
let response: DanceResponse = send_dance(context, request)?;
if response.status_code == ResponseStatusCode::Ok {
println!("Commit succeeded!");
}
Builders are the preferred way to trigger standardized Dances that cross the membrane boundary.
13. Access Control¶
All reference operations are validated against an AccessType:
| AccessType | Transient | Staged | Smart |
|---|---|---|---|
| Read | ✅ | ✅ | ✅ |
| Write | ✅ | ✅ | ❌ |
| Clone | ✅ | ✅ | ✅ |
| Commit | ❌ | ✅ | ❌ |
| Abandon | ✅ | ✅ | ❌ |
Unauthorized actions raise HolonError::NotAccessible.
14. Error Handling¶
All functions return Result<_, HolonError>.
Common variants include:
NotAccessible– operation not permitted in this phaseInvalidHolonReference– bad or missing referenceInvalidType,UnexpectedValueType,EmptyField– schema or data errors
Errors returned through Dances can also be mapped to ResponseStatusCode.
15. Style & Conventions¶
- Use explicit suffixes (
*_reference) for reference variables. - When APIs expect
Vec<HolonReference>, wrap explicitly:
HolonReference::Transient(transient_reference)
16. Where to Look¶
- Prelude:
holons_prelude::prelude - Context behavior:
reference_layer/context_behavior.rs - Operations API:
reference_layer/holon_operations_api.rs - Reference traits and types:
reference_layer/holon_reference.rs,readable_holon.rs,writable_holon.rs,staged_reference.rs,transient_reference.rs - Access and state:
core_shared_objects/holon/state.rs - Dance builders:
holon_dance_builders - Query layer:
reference_layer/query_api.rs
17. Evolving Areas¶
- Validation runs primarily at commit.
- Fluent chaining (
&Self) is being standardized in setters. - The query API will expand to richer predicates.
- Additional Dances (Loader, Validation) will reuse the same reference layer interface.