Skip to content

feat(sdk): stage 6 — sub-composition scoped ids (F9)#1434

Open
vanceingalls wants to merge 1 commit into
sdk-stage5-adapter-exportsfrom
sdk-stage6-scoped-ids
Open

feat(sdk): stage 6 — sub-composition scoped ids (F9)#1434
vanceingalls wants to merge 1 commit into
sdk-stage5-adapter-exportsfrom
sdk-stage6-scoped-ids

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements F9: sub-composition scoped ids — a fully-qualified addressing scheme that lets callers target elements inside inlined sub-compositions unambiguously, even when bare data-hf-id values collide across sub-comp boundaries.

Design

After inlineSubCompositions flattens the tree, the DOM contains data-composition-file attributes that mark where each sub-composition's content begins. The SDK uses this to assign each element a scopedId: a /-joined chain of host ids ending in the bare leaf id.

  • Top-level: scopedId === id
  • Sub-comp element: "hf-HOST/hf-LEAF" (any depth: "hf-A/hf-B/hf-LEAF")
  • Host element itself stays in parent scope (bare id — it IS addressed from outside)

Host boundary detection (isNewHostBoundary): an element is a new scope when it has data-composition-file AND its value differs from its parent's. This correctly handles the outerHTML innerRoot edge case where both host and innerRoot share the same dcf value — only the HOST counts as the boundary.

Changes

File Change
engine/model.ts resolveScoped() + isNewHostBoundary()
types.ts HyperFramesElement.scopedId: string
document.ts buildElement carries scopePrefix, detects host boundaries
engine/patches.ts RFC 6902 escapeIdForPath/decodePathSegment for scoped ids containing /; all path builders + pathToKey/keyToPath updated
session.ts getElement matches by scopedId; find() returns scopedId; orphan cleanup decodes RFC 6902 before key compare; preserves removal markers, purges property sub-keys
engine/mutate.ts All element handlers use resolveScoped instead of findById; handleRemoveElement collects full subtree hf-ids for GSAP cascade (Q3 fix); validateOp uses resolveScoped
session.subcomp.test.ts 20 new contract tests

Test coverage (20 tests)

  • resolveScoped: bare id, scoped id, collision prevention, 3-level, null cases
  • ElementSnapshot.scopedId: top-level, sub-comp, host itself, 3-level, same sub-comp twice, outerHTML innerRoot
  • Dispatch to scoped target: mutation hits correct element, patch path encoding, getElement by scopedId, find() returns scopedIds
  • Override-set keys: scoped key produced, removal marker preserved, orphan purge on removeElement
  • Serialize stability: scopedIds identical after serialize + re-open

Stack position

Stacked on PR #1432 (stage 5 — adapter exports), which is stacked on PR #1429.

Adds fully-qualified scoped ids for addressing elements inside inlined
sub-compositions, so callers can target "hf-HOST/hf-LEAF" unambiguously
even when bare hf-ids collide across sub-composition boundaries.

Changes:
- model.ts: resolveScoped() traverses id segments through nested subtrees;
  isNewHostBoundary() detects host boundaries (dcf ≠ parent dcf handles
  outerHTML innerRoot edge case)
- types.ts: HyperFramesElement gains scopedId field
- document.ts: buildElement carries scopePrefix, propagates childPrefix
  at host boundaries; buildRoots starts with ""
- patches.ts: RFC 6902 escapeIdForPath / decodePathSegment for scoped ids
  containing "/"; all path builders and pathToKey/keyToPath updated
- session.ts: getElement() matches by scopedId; find() returns scopedIds;
  orphan cleanup decodes RFC 6902 before key comparison, preserves removal
  markers, purges property sub-keys for both bare and scoped ids
- mutate.ts: all element handlers use resolveScoped instead of findById;
  handleRemoveElement collects full subtree hf-ids before removal for
  complete GSAP animation cascade (Q3 fix); validateOp uses resolveScoped

20 new contract tests in session.subcomp.test.ts covering resolveScoped,
scopedId propagation, dispatch to scoped targets, RFC 6902 patch encoding,
override-set key format, orphan purge, and serialize stability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant