Mu-Affordances and Microconcepts¶
Document Version: 0.1
Status: DESIGN NOTE - vocabulary and implementation direction only
Prior art: issue #113, issue #141, scratch/mechanics/badge/badge.py,
engine/src/tangl/prose/mu_block.py, engine/src/tangl/prose/dialog.py
Relevant layers: tangl.core, tangl.vm.provision, tangl.story.concepts,
tangl.story.episode, tangl.prose
Problem Statement¶
StoryTangl already has several objects that are clearly meaningful and structured, but do not want full graph identity:
dialog sub-blocks that carry speaker/style metadata before rendering
badge-like grants that should follow an assigned provider while a link exists
temporary titles, uniforms, avatars, and similar role-linked decorations
small provider-side annotations created by planning or provisioning
These objects are “less identified than an Entity” but more meaningful than plain dict payloads.
They need to be:
authored or generated in a managed way
passed through behaviors and handlers
filtered or ordered deterministically
projected into namespace, render output, or effective affordances
discarded cleanly when their context no longer applies
This note refers to that family as microconcepts, and to the provider-binding subset as mu-affordances.
Core Insight¶
A microconcept is not a small Entity. It is a context-bound concept carrier with no standalone graph identity.
An Entity has stable identity because it must be registrable, matchable, and addressable outside the immediate operation that is using it.
A microconcept is different:
it is meaningful only relative to a caller, source edge, provider, or render pass
it may have provenance such as
source_idorsubject_idit does not belong in the graph registry as an independent peer
it can still carry labels, tags, locals, media hints, and ordering metadata
In other words, it has context identity, not graph identity.
This is the common thread between:
MuBlockin discourse parsingold dynamic badges
role-linked derived grants from issue
#141future provider overlays created by dependencies, affordances, or fanout
Why “Fragment” Is Not the Right Word¶
“Fragment” already has a strong meaning in StoryTangl: rendered or journal-ready output records such as content fragments, dialog fragments, media fragments, and control fragments.
That meaning is useful and should stay stable.
Microconcepts come before fragment output, or may never become fragments at all. They can be used to:
enrich a namespace
decorate a provider view
attach effective tags or media
drive dynamic affordance projection
parse authored content into more structured intermediate units
So:
Fragment should remain “rendered output artifact”
Microconcept should mean “context-bound entity-like concept carrier”
MuBlock is a render-focused microconcept subtype
MuAffordance is a provider-binding microconcept subtype
“Component” is possible vocabulary, but it is already overloaded elsewhere in engine design. “Microconcept” is the clearer story-facing umbrella.
Existing Prior Art¶
1. MuBlock in discourse parsing¶
MuBlock in engine/src/tangl/prose/mu_block.py already expresses the
basic pattern well:
smaller than a block
not persisted in the graph
carries just enough metadata to behave meaningfully
promoted into a real output artifact through
to_fragment()
DialogMuBlock in engine/src/tangl/prose/dialog.py shows the same pattern
applied to speaker attribution and dialog styling.
This is the clearest proof that StoryTangl already benefits from managed non-entity intermediates.
2. Dynamic badges¶
The old badge system in scratch/mechanics/badge/badge.py is the “ur
mu-affordance” design.
Its enduring ideas are:
singleton-authored definitions
dynamic attach / detach semantics
topological dependency ordering
hide / supersede relationships
projection into both condition context and render output
Its weak point was not the concept. The weak point was the old plumbing:
explicit mutation of node associations
global recomputation scans
tag-like state being pushed into nodes instead of derived from current context
3. Role-linked grants¶
Issue #141 captures the concrete modern use case:
a role edge resolves to some provider
while that edge exists, the provider should gain contextual properties
when the provider changes, the contextual properties should move with it
nothing should rely on brittle link / unlink scripts staying in sync
That is exactly what a mu-affordance is:
declared on a carrier edge
bound to the currently assigned provider
projected as derived state
discarded when the assignment changes
Definition¶
Microconcept¶
A microconcept is a serializable or constructible value object that:
is carried by or derived from a parent entity, edge, template, or render pass
has no standalone registry identity
is only meaningful in a specific bound context
may be promoted into another runtime artifact such as a namespace view, effective tag set, affordance decoration, or fragment
Bound Microconcept¶
A bound microconcept is a runtime binding of a microconcept to a specific context, such as:
source node
carrier edge
resolved provider
current caller
render source id
This is the object that actual handlers consume.
Mu-Affordance¶
A mu-affordance is a bound microconcept that decorates or exposes a provider through a relationship.
Examples:
boss.title == "boss"while a role edge is activea provider temporarily gains a
uniformmedia item through assignmenta selected provider gains extra tags or labels while filling a task
a fanout-created affordance carries extra provider-facing menu metadata
MuBlock¶
A MuBlock is a microconcept that specializes in render-oriented content structure and usually promotes into fragments.
Proposed Shape¶
The clean shape is two-tiered:
2. Runtime binding¶
A lightweight bound view with context attached:
carriersubjectcallersource_idmerged or effective
localseffective
tagsprojected media or narrative annotations
Handlers then consume this binding without pretending it is a graph peer.
Relationship to Existing Engine Layers¶
Core / VM¶
The VM does not need a brand new universal “micro task” system to support the useful parts of this idea.
The immediate path is to reuse existing handler surfaces:
on_gather_nsfor contextual property exposureplanning / provisioning for dynamic provider-side affordances
journal or content gather handlers for render projection
This is enough to support the first meaningful applications.
Story¶
Story layer code is where microconcepts become narratively legible:
roles grant titles and avatars
affordances expose badge-like temporary status
anonymous blocks or discovered providers may contribute contextual metadata
block content can parse into dialog or card-like micro-blocks before render
Discourse¶
Discourse parsing already has the clearest example of microconcept promotion:
MuBlock -> Fragment
That same promotion shape can later support:
MuAffordance -> BoundProviderViewMuAffordance -> EffectiveTagOverlayMuAffordance -> Media projection
Preferred Implementation Strategy¶
Phase 1: provider-bound grants¶
Start with the narrowest and most valuable slice from issue #141.
Add edge-carried grant specs on story relationships such as
RoleBind those grants to the currently resolved provider
Expose them through namespace gathering as a contextual provider view
Keep the grants derived, not persisted as mutable provider state
This solves the motivating examples:
title
rank
badge
avatar
uniform
Phase 2: effective overlay helpers¶
Add a small set of helpers for consuming bound microconcepts:
effective locals
effective tags
effective media
precedence / supersession ordering
This is the modern replacement for old dynamic badge mutation.
Phase 3: relation to fanout¶
Allow planning-time created affordances to carry microconcept-like metadata that binds to the selected provider.
This matters for:
menu hubs
sandbox hubs
gathered provider choices
future actor / location roster views
Phase 4: evaluate common base with MuBlock¶
Only after the first two slices are working should StoryTangl decide whether
MuBlock and provider-side microconcepts want a shared base implementation or
just shared vocabulary.
The concept is shared already. The code does not need to be prematurely unified.
What Should Not Be in Scope Yet¶
Issue #113 bundled several larger ideas together. They should remain separate
from the first mu-affordance implementation:
a universal heavy-task / light-task dispatch split
a complete replacement of existing VM phase wiring
global achievements or reactive rule engines
generic enter / exit micro-dependency scripting
persistent snapshotting of derived microconcept state
Those may later use the same vocabulary, but they are not required to realize the main value here.
Design Rules¶
1. Derived beats stored¶
If a property can be computed from current topology and current context, prefer derived projection over mutating stored state.
2. Promote only when necessary¶
If something needs durable identity, inventory presence, or independent mutable state, it should become a real Entity or Token. Otherwise it should remain a microconcept.
3. Context is part of the contract¶
Microconcepts are not free-floating. The binding context is what makes them meaningful.
4. Fragment remains output vocabulary¶
Do not overload “fragment” for provider overlays or relationship-bound grants.
5. Story semantics live above the generic layer¶
The generic layer should know about binding and projection. Story layer code should know about titles, avatars, uniforms, badge text, and dialog speakers.
Example Story Use Cases¶
Role-linked title¶
roles:
- label: boss
selector:
has_kind: Actor
grants:
title: "boss"
tags: ["management"]
While the role is bound, the namespace can expose:
bossboss_titleboss.tags
without mutating the underlying actor permanently.
Role-linked avatar or uniform¶
roles:
- label: foreman
selector:
has_kind: Actor
grants:
media:
- name: "foreman_uniform.svg"
media_role: "avatar_im"
The assigned provider temporarily gains visual presentation metadata through the role relationship.
Dialog parsing¶
Authored block text can parse into DialogMuBlock items which later render into
attributed journal fragments, carrying speaker and discourse metadata without
turning each utterance into a graph node.
Open Questions¶
Should the generic implementation call these
MicroconceptSpec/BoundMicroconcept, or use a more VM-neutral name likeAttachmentSpec/BoundAttachment?Should bound provider views be proxy objects, plain mappings, or lightweight wrappers with explicit accessors?
How should multiple active mu-affordances resolve conflicts for the same key: priority, scope, order of attachment, or explicit policy?
Should effective tags become selector-visible everywhere, or only through specific helper paths?
How much shared code should
MuBlockand provider-bound microconcepts actually have, versus just sharing terminology?
Proposed Near-Term Outcome¶
The near-term goal is not a giant “micro-behavior” subsystem.
The near-term goal is a clean way to represent managed entity-like things with less identity than Entities, so StoryTangl can:
bind temporary properties to providers through relationships
carry small conceptual units through the VM without full graph registration
parse authored content into structured intermediate units
keep rendered fragments as the final output vocabulary
That would consolidate the useful core of issues #113 and #141 while
aligning with the old badge work and the already-working MuBlock pattern.