DEV Community

Sui Gn
Sui Gn

Posted on

An Introduction to .me - Inverted Dependency Indexing

description: How .me tracks reactive dependencies from leaves back to derived semantic paths.

An Introduction to .me - Inverted Dependency Indexing

.me is a semantic kernel built around paths.

You can write:

me.order.price(100);
me.order.quantity(5);
me.order["="]("total", "price * quantity");
Enter fullscreen mode Exit fullscreen mode

And read:

me("order.total"); // 500
Enter fullscreen mode Exit fullscreen mode

At the surface, this looks like a free-form object.

Underneath, it is a reactive dependency graph.

The important part is how .me updates derived values without scanning the entire tree.

That mechanism is Inverted Dependency Indexing.

The Problem

In a normal tree, data flows from root to leaf:

order
  price
  quantity
  total
Enter fullscreen mode Exit fullscreen mode

A naive reactive system might handle a mutation by asking:

What changed?
What depends on it?
Do I need to scan the tree?
Do I need to recompute all formulas?
Enter fullscreen mode Exit fullscreen mode

That becomes expensive as the graph grows.

If the system has N paths, a global scan makes the cost of one local update depend on total graph size.

.me avoids that by indexing dependencies in the opposite direction.

The Inversion

When a derived value is declared:

me.order["="]("total", "price * quantity");
Enter fullscreen mode Exit fullscreen mode

.me records:

order.total depends on:
  order.price
  order.quantity
Enter fullscreen mode Exit fullscreen mode

But it also builds the reverse mapping:

order.price    -> order.total
order.quantity -> order.total
Enter fullscreen mode Exit fullscreen mode

That reverse mapping is the inverted dependency index.

In the TypeScript kernel, this is represented by refSubscribers:

refSubscribers: Record<string, Set<string>>
Enter fullscreen mode Exit fullscreen mode

Conceptually:

refSubscribers["order.price"].add("order.total");
refSubscribers["order.quantity"].add("order.total");
Enter fullscreen mode Exit fullscreen mode

Now a write does not need to search the graph.

When this happens:

me.order.price(200);
Enter fullscreen mode Exit fullscreen mode

the kernel can directly ask:

Who subscribes to order.price?
Enter fullscreen mode Exit fullscreen mode

And the answer is already indexed:

order.total
Enter fullscreen mode Exit fullscreen mode

Registration Time

The work starts when a derivation is registered.

.me extracts references from the expression:

"price * quantity"
Enter fullscreen mode Exit fullscreen mode

Then resolves them relative to the current path:

price    -> order.price
quantity -> order.quantity
Enter fullscreen mode Exit fullscreen mode

Then stores the derivation target:

order.total
Enter fullscreen mode Exit fullscreen mode

The derivation record contains:

expression
evaluation scope
resolved refs
last computed time
Enter fullscreen mode Exit fullscreen mode

The inverted index contains:

ref path -> dependent target paths
Enter fullscreen mode Exit fullscreen mode

This is the key shift.

The kernel does not wait until mutation time to discover the dependency structure. It builds the structure when the derivation is declared.

Mutation Time

When a value changes:

me.order.price(200);
Enter fullscreen mode Exit fullscreen mode

the kernel calls its invalidation path.

In eager mode, the algorithm is:

1. start from changed path
2. look up subscribers in refSubscribers
3. recompute those targets
4. push recomputed targets back into the queue
5. continue until the affected frontier is done
Enter fullscreen mode Exit fullscreen mode

So if another derivation depends on order.total, it can continue bubbling upward:

me.order["="]("total", "price * quantity");
me.order["="]("withTax", "total * 1.16");
Enter fullscreen mode Exit fullscreen mode

A price mutation affects:

order.price
  -> order.total
    -> order.withTax
Enter fullscreen mode Exit fullscreen mode

The kernel follows that chain directly.

It does not scan unrelated paths.

O(k), Not O(N)

The practical complexity is based on k, the affected dependency frontier.

N = total graph size
k = number of derived nodes affected by this mutation
Enter fullscreen mode Exit fullscreen mode

If the graph has one million paths but only three derived nodes depend on the changed value, the mutation should touch those three derived nodes, not the full million.

That is the point of Inverted Dependency Indexing:

cost(update) ~= O(k)
Enter fullscreen mode Exit fullscreen mode

Not:

cost(update) ~= O(N)
Enter fullscreen mode Exit fullscreen mode

This is not magic. If one path has 100,000 subscribers, then k = 100,000, and the update is expensive. The optimization works when dependencies are local and the affected frontier stays small.

Eager vs Lazy

.me supports two recomputation modes.

In eager mode, mutation immediately recomputes affected subscribers.

write source
  -> find subscribers
  -> recompute now
Enter fullscreen mode Exit fullscreen mode

In lazy mode, mutation only bumps reference versions.

write source
  -> mark dependency version changed
  -> recompute later on read
Enter fullscreen mode Exit fullscreen mode

The runtime control lives under the reflective plane:

me["!"].runtime.setRecomputeMode("lazy");
Enter fullscreen mode Exit fullscreen mode

Then, when a derived value is read:

me("order.total");
Enter fullscreen mode Exit fullscreen mode

the kernel checks whether its dependency versions are stale. If they are stale, it recomputes the target before returning the value.

So eager mode pushes work into writes.

Lazy mode pulls work into reads.

Explainability

Because recomputation is tracked as a wave, .me can explain what happened.

me["!"].explain("order.total");
Enter fullscreen mode Exit fullscreen mode

A derived path can report:

expression
dependency inputs
last recompute wave
k
source path
Enter fullscreen mode Exit fullscreen mode

For example:

order.total
  expression: price * quantity
  depends on:
    order.price
    order.quantity
Enter fullscreen mode Exit fullscreen mode

This matters because a reactive system should not be a black box. If a value changed, the kernel should be able to show the path of causality.

Secret Scopes

The same model can operate across secret branches.

.me supports path-bound secret scopes:

me.wallet["_"]("wallet-key");
me.wallet.balance(12480);
Enter fullscreen mode Exit fullscreen mode

The root remains stealth:

me("wallet"); // undefined
Enter fullscreen mode Exit fullscreen mode

But internal derived logic can still use the encrypted branch under the kernel’s rules.

When explain() crosses a secret input, it masks the value instead of exposing it:

origin: "stealth"
value: "●●●●"
Enter fullscreen mode Exit fullscreen mode

So dependency tracking and structural silence can coexist.

The dependency graph still works, but public inspection does not leak private values.

The Mental Model

In a regular index:

path -> value
Enter fullscreen mode Exit fullscreen mode

In an inverted dependency index:

source path -> derived targets
Enter fullscreen mode Exit fullscreen mode

So .me keeps both ideas:

semantic path       -> current value
dependency ref path -> subscribers
Enter fullscreen mode Exit fullscreen mode

The first lets the kernel read meaning.

The second lets the kernel update meaning efficiently.

Why It Matters

.me starts with free-form paths:

me.whatever.you.want("x");
Enter fullscreen mode Exit fullscreen mode

But once paths can derive from other paths, the runtime needs a way to preserve consistency.

Inverted Dependency Indexing is that mechanism.

It lets .me behave like a living semantic graph without treating every mutation as a whole-graph event.

The design is simple:

declare meaning freely
record dependencies when formulas are created
invert those dependencies into subscribers
update only the affected frontier
explain the recompute path when needed
Enter fullscreen mode Exit fullscreen mode

That is the core of .me reactivity:

free-form semantics on the outside
indexed dependency causality underneath
Enter fullscreen mode Exit fullscreen mode

Docs
sui Gn
Inverted Dependency Indexing

Top comments (0)