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_id or subject_id

  • it 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:

  • MuBlock in discourse parsing

  • old dynamic badges

  • role-linked derived grants from issue #141

  • future 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 active

  • a provider temporarily gains a uniform media item through assignment

  • a 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:

1. Authored spec

A small declarative object stored on some carrier:

  • node

  • edge

  • template payload

  • content parser output

Possible fields:

  • label

  • kind

  • priority

  • conditions

  • locals

  • tags

  • media

  • hides

  • source_ref or other authored selectors

This spec has no graph UID of its own.

2. Runtime binding

A lightweight bound view with context attached:

  • carrier

  • subject

  • caller

  • source_id

  • merged or effective locals

  • effective tags

  • projected 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_ns for contextual property exposure

  • planning / 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 -> BoundProviderView

  • MuAffordance -> EffectiveTagOverlay

  • MuAffordance -> 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 Role

  • Bind 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:

  • boss

  • boss_title

  • boss.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 like AttachmentSpec / 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 MuBlock and 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.