# Holonic and the DOM: A Mental Model Readers familiar with the W3C Document Object Model often recognize structural similarities when they first encounter holonic graphs. The similarities are real and worth making explicit. This page maps the DOM's core concepts onto their holonic counterparts, identifies where the mapping holds cleanly, and flags where the two models diverge. ## Why the DOM is a useful mental model The DOM has been production infrastructure for nearly three decades. Its event model handles containment, opacity, propagation, and delegation at scale across every browser in existence. Anyone who has written a web application has built intuition about how events bubble up through nested elements, how `event.stopPropagation()` terminates propagation, and how a parent element can react to events fired deep inside a custom component it doesn't understand. Holonic graphs face an architecturally similar problem. A holarchy is a containment structure: holons can contain other holons (via `cga:memberOf`), parent-child relationships are declared in RDF, and parent holons should not need to introspect their children's interior graphs to know how to route behavior. These are the same constraints the DOM addresses. Borrowing the DOM's vocabulary gives newcomers a fast way to situate holonic ideas against a model they already trust. That said, the mapping is not exact. The DOM is synchronous; a federated holarchy may be asynchronous. The DOM is a strict tree; a holonic graph allows multiple portal paths. The DOM doesn't validate event payloads against schemas; holons can have SHACL-governed membranes. These differences matter and are covered in the "Where the mapping diverges" section below. ## Concept mapping The table shows how DOM concepts correspond to the current holonic library (0.4.0). Some correspond directly; some would require additional machinery the library does not currently provide. | DOM concept | Holonic counterpart | Status in 0.4.0 | |-------------|---------------------|-----------------| | Document | HolonicDataset | Direct correspondence | | Element | Holon | Direct correspondence | | Tree (parent-child) | Containment via `cga:memberOf` | Direct correspondence | | Opaque element interior | Holon interior graphs | Direct correspondence | | Element attributes | Interior graph triples about the holon IRI | Direct correspondence | | Shadow DOM boundary | Membrane (SHACL boundary) | Direct correspondence with extension (payload validation) | | Event target | Destination holon in a traversal | Direct correspondence | | Event dispatch | `HolonicDataset.traverse()` | Implicit; no explicit `dispatch_event` API | | Capture phase (parent-first) | Not explicit | Not implemented | | Target phase | Traversal's final CONSTRUCT + membrane validation | Direct correspondence | | Bubble phase (child-first) | Not explicit | Not implemented | | Event handlers | Portal CONSTRUCT queries + SHACL rules | Partial correspondence | | stopPropagation() | Not explicit | Not implemented | | preventDefault() | Traversal blocked by membrane validation failure | Implicit correspondence | | Event object | Not a first-class entity | Implicit; no `cga:Event` type | | addEventListener | Portal registration | Partial correspondence | | Custom events | Portal CONSTRUCT with caller-defined query | Direct correspondence | | Event bubbling terminator | End of path or explicit validation failure | Implicit correspondence | ## What the DOM model suggests about how holons should eventually be dispatched Cagle's framing (*LinkedIn discussion, April 2026*) proposes that plural-orchestrator coordination can be resolved by treating the holarchy as a DOM-like event propagation structure: events arrive at a holon, propagate through containment, and are consumed, delegated, or ignored. Under that view, dispatching behavior through a holarchy would work roughly as follows. An **external event source** produces an event directed at a holon. The event source is any `prov:Agent` — a scheduled job, an LLM agent turn, an external HTTP call, a state change somewhere else in the system. The framework does not care who originated the event. It cares only that the event arrived and is targeted at a specific holon. The event enters the holarchy at a **root or ancestor holon** and begins the **capture phase**: it walks down the containment chain toward the target, giving each ancestor a chance to intercept. An ancestor might log the event, redirect it, transform it, or stop it entirely. If no ancestor intercepts, the event reaches the target. At the **target phase**, the receiving holon's membrane validates the event payload. If validation passes, the portal's CONSTRUCT query fires and the target's interior is updated. If validation fails, the event is rejected. Either outcome is recorded as a `prov:Activity` in the target's context graph. The event then enters the **bubble phase**: it propagates back up through the containment chain, giving each ancestor a chance to react to what just happened. An ancestor might aggregate the event into its own metadata (a summary count, a health check), trigger a secondary traversal, or mark its own metadata as dirty. If no ancestor reacts, the event ends its lifecycle. **Unhandled events** — events that neither capture nor bubble phases react to — are legitimate. They arrived, they were considered, they were not acted on. The framework logs a minimal PROV-O record of the event's lifecycle ("arrived, considered, no handler") to preserve auditability. This is the main extension over the browser DOM, which silently discards unhandled events; a governed holonic system cannot be silent about what it chose not to do. This model is **not currently implemented** in 0.4.0. Portal traversal today is a targeted operation — the caller knows the portal and invokes it directly. There is no capture phase, no bubble phase, no explicit event object, no propagation semantics. Adopting the full DOM-style dispatch model would require new library machinery that is captured as OQ9 in `docs/SPEC.md`. ## Where the mapping diverges The DOM model maps cleanly for most structural concerns but strains in several places that matter for holonic systems. ### Synchronous vs asynchronous At first glance, DOM event dispatch looks synchronous: call `element.click()`, and the capture phase, target phase, and bubble phase run in a tight loop before control returns. This is the intuition most engineers carry about the DOM, and it's the intuition that makes the holonic mapping feel uncertain when a federated holarchy enters the picture. But the DOM is not actually a synchronous system. It is a **synchronous API layered over an asynchronous rendering engine**. The distinction matters because the holonic library faces an architecturally similar split, and naming it explicitly clarifies what a holonic dispatch model would need to preserve versus what it would need to resolve differently. #### Where the DOM is asynchronous Several regimes inside the browser operate on their own schedule, decoupled from the synchronous JavaScript call stack that manipulates the DOM: - **Initial parsing and loading.** As the browser receives HTML over the network, it constructs the DOM tree incrementally while simultaneously fetching scripts, stylesheets, images, and fonts. The DOM exists in a partial state for a non-trivial window before the document is fully loaded. JavaScript running during that window sees a DOM that is actively being built by another process. - **Rendering and painting.** When JavaScript mutates the DOM, the mutation is synchronous from the script's perspective — the node appears in the tree immediately. But the browser does not repaint on every mutation. It batches mutations, waits for the JavaScript call stack to empty, then runs layout, style recalculation, and paint in a separate pipeline. This is why `element.style.width = '100px'` followed immediately by reading `element.offsetWidth` can force a synchronous layout and why performance-sensitive code avoids that pattern. - **Async script loading.** `