Add minimal reproducible example for issue #3 with identical recursive DOM structures
Issue #3 describes DOM nodes without unique id attributes getting incorrectly combined during patching, particularly when removing middle items from lists. This manifests as wrong nodes being removed or content getting mixed up between similar elements.
What's Added
Interactive demo application (examples/patching-bug-repro/)
- Demonstrates the bug with recursive nested query structures modeled after gobaith
-
Recursive query types:
Query = Filter | And(Query, Query) | Or(Query, Query)- queries can contain other queries infinitely - 6-7+ levels of nesting at each recursive level with branch wrappers, indent containers, combinator selectors, and triple-nested filter inputs
-
IDENTICAL query structures for reliable reproduction: All 3 initial queries use the same
And(Filter, Filter)pattern:- Query 1:
And(Filter(status, active), Filter(priority, high)) - Query 2:
And(Filter(category, bug), Filter(severity, critical)) - Query 3:
And(Filter(type, feature), Filter(phase, development))
- Query 1:
- Distinguishable queries only by their field values (status, priority, category, severity, type, phase) to clearly show when content gets mixed up
- Visual AND combinator badges between branches
- Toggle between buggy (no IDs) and fixed (with IDs) versions via
useBuggyVersionflag - Visual reproduction matching issue description
Playwright test suite
- Automated reproduction of the bug with recursive nested structures
- Screenshot capture at each step for debugging
- Programmatic verification of input field values getting mixed between queries
Documentation
- Root cause explanation (patching algorithm in
src/coed.tslines 1042-1055 only compares tag + id) - Detailed explanation of why identical structures are needed to reproduce the bug
- Workaround guidance
- Setup instructions for manual and automated testing
The Bug
The bug is particularly evident with recursive tree structures that have IDENTICAL patterns. All queries must use the same recursive structure (e.g., all use And(Filter, Filter)) for the bug to manifest reliably:
// Recursive query types (like gobaith)
type FilterQuery = { kind: "Filter"; field: string; value: string; operator: string };
type AndQuery = { kind: "And"; left: Query; right: Query };
type OrQuery = { kind: "Or"; left: Query; right: Query };
type Query = FilterQuery | AndQuery | OrQuery; // Recursive!
// All queries use And(Filter, Filter) - IDENTICAL structure
// Buggy - recursive nodes without IDs get confused during patching
coed.div([], [coed.class_("filter-query")], [ // NO ID - causes bug!
coed.div([], [coed.class_("query-header")], [...]),
coed.div([], [coed.class_("combinator-container")], [
coed.div([], [coed.class_("left-branch")], [
coed.div([], [coed.class_("branch-wrapper")], [
coed.div([], [coed.class_("indent")], [
renderQueryBuilder(query.left, ...) // RECURSIVE CALL
])
])
]),
coed.div([], [coed.class_("combinator-selector")], [
coed.span([], [], [coed.text("AND")]) // All queries have "AND"
]),
coed.div([], [coed.class_("right-branch")], [
coed.div([], [coed.class_("branch-wrapper")], [
coed.div([], [coed.class_("indent")], [
renderQueryBuilder(query.right, ...) // RECURSIVE CALL
])
])
])
])
]);
// Fixed - unique ID allows proper patching
coed.div([], [
coed.class_("filter-query"),
coed.attribute("id", `filter-query-${index}`) // WITH ID - works!
], [
// ... same recursive structure
]);
Why Identical Structures Are Needed
When removing Query 2 from the middle:
- Old state: [Q1, Q2, Q3, Q4] at indices [0, 1, 2, 3]
- New state: [Q1, Q3, Q4] at indices [0, 1, 2]
- The patching algorithm patches old-DOM-at-index-1 with new-virtual-tree-at-index-1
- Since all queries have IDENTICAL structure (same tags, classes, nesting) and NO IDs, the algorithm patches children in place instead of replacing
- Result: Query 3's content appears in Query 2's DOM location!
Usage
cd examples/patching-bug-repro
npm install && npm run serve # Manual testing at http://localhost:8000
npx playwright install chromium # One-time setup
npm test # Automated reproduction
Screenshots
Initial state with 3 queries using identical And(Filter, Filter) structure:
All queries have the same recursive pattern - only the field names and values differ.
After adding Query 4 and removing Query 2 - demonstrates the bug:
When you remove Query 2 (which contains category=bug AND severity=critical), the patching algorithm gets confused by the identical recursive structures:
- Query 2 now shows
type=feature AND phase=development(which was Query 3's content) - Query 3 now shows
field=value AND status=active(which was Query 4's content)
The input field values get mixed up between queries because the identical recursive tree nodes without unique IDs cause the patching algorithm to confuse which node is which.
This provides a debugging harness for investigating and fixing the patching logic issue with identical recursive nested DOM trees, matching the complexity found in real-world applications like gobaith.
Original prompt
Please create a minimial reproduceable code snippet for the issue decribed here: https://github.com/eeue56/coed/issues/3
Use Playwright with @eeue56/coed in order to create an example that I can use for debugging the issue
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.