The composite showed a map and a grid that ignored each other: filtering the grid
to Africa moved its own count 2,107 → 315 while the map beside it kept every pin.
The composite now owns ONE filter state; the children report theirs upward as DOM
events and accept ours downward as attributes, so each stays independently
embeddable — a child mounted alone hears nothing and is handed nothing.
MEASURED on a served built tree, region facet:
grid → map 2,107 → 315, and the map's own state follows (externalRegion and
selectedRegion both 'africa'); clearing returns both
map → grid the map's map-filtered contract → the grid's list 2,107 → 315;
clearing returns it
standalone grid alone 24 rows / Total 2,107 / 0 errors; map alone canvas 1 /
0 errors — unchanged
MUTATION disabling the composite's push leaves the grid filtering itself
(315) while the map stays empty, and kills the reverse direction:
the check fails when the link is broken
Four things this cost, each of which first looked like it worked:
1. StackLayout applies `attrs` ONCE at create — its documented contract. The
first version recomputed `sections`; the composite adopted the filter and the
map's attribute in the DOM never changed. A computed nobody re-reads is not a
channel, so the state is now PUSHED onto the mounted children.
2. The echo cancelled itself. Composite pushes → the grid re-filters → it emits
groups-filtered with activeFilters still EMPTY → the composite adopts that as
"the reader cleared it" and wipes what it just applied. Both handlers were
correct; the round trip undid itself in under a second. A push is not a user
action, so the push window ignores children.
3. The grid ignored host filters BY DESIGN when its own dropdowns are visible:
activeFilters picks local OR props, never both. Right for the desktop
composite, which hides them; silently wrong here. A host facet now seeds the
LOCAL state, so the map's selection appears as a chip in the grid's own bar
and the reader can clear it there.
4. That seeding called lbl(), which is local to another computed — "lbl is not
defined", watcher dead, attribute set, list unchanged. Visible only after the
probe was widened to console.error. A silent watcher failure looks exactly
like a feature that was never wired.
SCOPE, DELIBERATE: region only. The map filters religion and language by the
canonical strings its own data carries; the grid filters them by slugs from a
different table. Linking those means inventing a mapping, and a wrong link is
worse than an honest gap.
The map also gains a verification handle (window.__researchMapFilterState), the
same contract as __rdashMap and __grMap: a linked filter is a STATE claim, and
every DOM proxy for it was either tab-specific or a selector guess.