Skip to content

duplicate_composition_id errors on the documented multi-instance sub-composition pattern #3403

Closed

Description

@miguel-heygen

Describe the bug

duplicate_composition_id reports an error on the multi-instance sub-composition pattern
that sub-compositions.md documents. Everything else in the pipeline handles that pattern
correctly, so the only thing standing in the way is the lint rule.

Reported as:

lint duplicate_composition_id false-positive: repeating the same sub-composition N times
in one host (data-composition-src, same data-composition-id per sub-compositions.md
'Per-Instance Variables') trips this rule as an error, even though
render/validate/inspect/snapshot all handle it correctly (confirmed via snapshot). Pattern:
word-per-instance captions, e.g. compositions/word-caption.html mounted 8x with different
data-variable-values on one track. Rule has no exemption for elements carrying
data-composition-src.

The documented pattern it rejects

skills/hyperframes-core/references/sub-compositions.md:220 — "Per-Instance Variables":

<div
  data-composition-id="data-chart"
  data-composition-src="compositions/data-chart.html"
  data-variable-values='{"title":"Q4 Revenue","accent":"#66d9ef"}'
  ...
></div>

The host can render the same sub-composition multiple times with different
data-variable-values to produce per-instance variations.

Doing exactly that produces N elements sharing one data-composition-id, which is what the
rule errors on.

Root cause

packages/lint/src/rules/composition.ts:268 buckets every element by data-composition-id
and reports any bucket with two or more members:

for (const tag of tags) {
  if (isInsideInertTemplate(tag, tags)) continue;
  const compositionId = readDecodedAttr(tag.raw, "data-composition-id");
  if (!compositionId || compositionId.trim().length === 0) continue;
  ...
}
...
if (matchingTags.length < 2) continue;

The rule has no awareness of data-composition-src — zero references to it in the rule
body. It cannot tell a composition root (where the id must be unique) from a mount of a
sub-composition (where repeating the id is the documented way to get per-instance
variations).

The rule's own message and fixHint show what it was built for — a <meta> tag carrying the
same id as the root <div>:

"Keep data-composition-id on exactly one element, the composition root. Remove it from
metadata or duplicate hosts, especially a <meta> tag carrying the same
data-composition-id as the root <div>, which causes a silent duplicate-id collision."

That is a real collision worth catching. Repeated mounts are not the same thing, and the
runtime already treats them differently: assignRuntimeCompositionIds rewrites repeated
mounts to id__hf1, id__hf2, and so on, precisely so they can coexist.

Suggested fix

Only consider elements that are not sub-composition mounts when looking for duplicates —
i.e. skip any tag carrying data-composition-src, the same way the rule already skips tags
inside an inert <template>.

That keeps the meta-versus-root collision the rule exists for, and stops it contradicting the
documented per-instance pattern.

Regression tests worth having: the documented pattern (same id, multiple
data-composition-src mounts, differing data-variable-values) must be clean, and the
original <meta>-versus-root collision must still error.

Severity note

This is an error, so it blocks check. A user following our own documentation gets a
failing gate and no correct way to satisfy it short of abandoning the pattern.

Environment

hyperframes  0.8.6
Affected     packages/lint/src/rules/composition.ts:268
Docs         skills/hyperframes-core/references/sub-compositions.md:220
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions