fix(sdk): agree removeElement/getElement on duplicate bare ids (#1511)

* fix(sdk): setStyle removes hyphenated properties (was kebab/camel key mismatch)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sdk): agree removeElement/getElement on duplicate bare ids

A bare hf-id duplicated across a sub-composition element and a top-level
element resolved to different instances: removeElement → resolveScoped →
querySelector (document-order-first, the inner sub-comp dup) while getElement
preferred the canonical match (scopedId === id, the top-level dup). So
removeElement(bareId) removed the inner instance and getElement(bareId) still
found the surviving top-level one — they disagreed.

resolveScoped now resolves an ambiguous BARE id to the canonical (top-level)
instance via isCanonicalScope (walks ancestors for isNewHostBoundary), falling
back to document order when no canonical match exists — matching getElement.
Fully-scoped paths (hf-host/hf-dup) and non-duplicated bare ids are unchanged.

Surfaced by SDK shadow parity (op:delete expected removed, actual present).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-06-16 12:30:54 -07:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 066ea798b4
commit cc055f318d
3 changed files with 126 additions and 9 deletions
+7 -4
View File
@@ -312,12 +312,15 @@ describe("runShadowDelete", () => {
</div>
</body></html>`;
it("CORRECTLY reports present/removed for the SDK duplicate-bare-id delete divergence", async () => {
it("reports clean delete for a duplicate bare id (SDK resolves removeElement/getElement to the same instance)", async () => {
const session = await openComposition(DUP_ID_HTML);
runShadowDelete(session, "hf-dup");
// removeElement dropped the inner instance; the top-level one survives, so the
// readback truthfully flags one mismatch (not silently passed).
expect(lastShadow()).toMatchObject({ op: "delete", dispatched: true, mismatchCount: 1 });
// SDK fix (agree removeElement/getElement on duplicate bare ids): both now
// resolve a bare id to the canonical (top-level) instance, so removeElement
// drops exactly the element the readback checks → no mismatch. (Previously
// removeElement dropped the inner instance while the top-level survived,
// which this shadow correctly flagged; that divergence is now fixed.)
expect(lastShadow()).toMatchObject({ op: "delete", dispatched: true, mismatchCount: 0 });
});
});