PR #600 Dormant Alpine Primitives¶
📜 Historical snapshot — not current docs
Captured 2026-04-20 during Dazzle's autonomous-improvement cycles. It records the framework as it was then and the gap being worked at the time; it may not describe current behaviour. Start from the documentation home, or see Project Evolution for how these fit together.
Date: 2026-04-20 (cycle 287, following cycle 286 Heuristic-1 save) Class: Framework governance / dead-code hygiene Status: Open — needs product-direction decision
Problem statement¶
PR #600 ("migrate dz.js UI features to Alpine.js") shipped a set of general-purpose Alpine primitives intended for reuse across the framework. Two of those primitives have zero production consumers today, despite being contracted, tested via the widget harness (UX-020), and ready for adoption:
components/alpine/dropdown.html— generic menu dropdown with click-outside dismiss + Escape-key handler + 3-branch item renderer (href / hx_delete / placeholder). Contract: cycle 286'salpine-dropdown.mddormant-governance doc. Consumer count: 0.components/alpine/confirm_dialog.html— native-<dialog>confirmation dialog withdzConfirmAlpine controller +dz-confirmwindow-event API. Contracted as UX-014 in cycle 130, QA'd via the UX-020 widget harness in cycle 175. Consumer count: 0 in production templates.
Cycle 286 discovered #1 during a Heuristic-1 audit — cycle 237's
coverage-map claim of "42 call sites" for dropdown.html turned out
to be wrong; actual production consumers = zero. Cycle 287 discovered
2 during a follow-up orphan-sweep — confirm_dialog.html has zero¶
production {% include %} consumers; the only references are the
component's own x-data="dzConfirm" anchor and the dev-only
test-event-widgets.html harness (UX-020).
The dzConfirm mechanism is particularly interesting because the
framework ships:
- The template (unused in production)
- The Alpine data component in dz-alpine.js:83 (hooks window
listener for dz-confirm events)
- An event-dispatch API that consumer code could use
($dispatch('dz-confirm', {message, action, method}))
So consumer code that dispatches dz-confirm expecting the dialog to
render would silently no-op — no dialog element exists in any
production-rendered page to pick up the event.
Evidence¶
- Cycle 286 log —
alpine/dropdown.htmlzero-consumer discovery via Heuristic 1. Cycle 237 claim was wrong. - Cycle 287 orphan-sweep — targeted grep for
components/alpine/*.htmlreferences acrosssrc/andtests/: slide_over.html→ 1 production consumer (filterable_table.html:327) — not orphaneddropdown.html→ 0 production consumersconfirm_dialog.html→ 0 production consumersdz-alpine.js:83—Alpine.data("dzConfirm", …)controller is defined + registers awindow.addEventListener("dz-confirm", …)test-event-widgets.html:213— the ONLY non-template reference todzConfirmor the dialog element. Dev harness, not a user-facing page.- SOURCES.txt — both orphan templates ship with the package's
Python wheel. They're present in every installed Dazzle but never
{% include %}'d by any rendering surface.
Root cause hypothesis¶
PR #600 was a framework-layer migration (dz.js → Alpine.js). The primitives were written as "available tools" for future UI features, but the feature-level consumers never landed. In the 2+ years since (cycle 237 coverage-map mentions these as active, but that map was optimistic — the actual call-site count was mis-measured), no product work has adopted either primitive.
The confirm-dialog specifically is probably a victim of HTMX's built-in
hx-confirm attribute being "good enough" for most delete flows —
which explains why no adopter ever replaced raw hx-confirm="Are you
sure?" text with the richer dialog. Similarly, dropdown usage in the
framework gets subsumed by context-menu, popover, and the per-row
action-link patterns already in use.
Not a defect. Not a drift class. Just dead-code governance debt.
Fix sketch¶
Three options, in increasing decisiveness:
Option A (minimal): explicit dormant annotation¶
Keep both templates + contracts, but add a "DORMANT: zero production consumers since PR #600" banner comment at the top of each template. Pros: preserves optionality if someone later wants to adopt. Cons: dead-code-that-says-it's-dead is still dead code, adds governance weight.
Option B (promotion): adopt by landing one consumer each¶
Find one legitimate feature that would benefit from each primitive and
wire it up. Examples:
- Dropdown could replace the inline 3-button row in list.html's
actions area (region_actions + CSV export) with a kebab-menu
dropdown. That collapses a widening horizontal toolbar into a single
trigger + menu.
- Confirm-dialog could replace hx-confirm="…" on destructive
bulk-action bar delete buttons. The dialog gives more space for the
destructive-action copy + a more prominent loading state than the
browser's default confirm prompt.
Pros: validates the primitives, adds UX polish to existing features.
Cons: each adoption is its own cycle's work; may surface design
questions (does the dropdown's 3-branch item shape actually fit the
list actions?).
Option C (deletion): remove as dead code¶
Delete both templates, delete the UX-014 + alpine-dropdown.md
contracts, delete the regression tests, delete the dzConfirm Alpine
component from dz-alpine.js, delete the harness wiring in
test-event-widgets.html. Also remove the PR #600 era references from
docs that mention these as "available" primitives.
Pros: lowest maintenance burden. Cons: throws away work; future
adopters would need to rebuild from scratch; harder to reverse if
someone later wants them back.
Blast radius¶
If Option A: zero behavioural change; comment additions only.
If Option B (adopt dropdown in list.html): touches 1 region
template + affects all 5 example apps' list views. Visual change — the
3-button actions row becomes a kebab menu. Needs design sign-off.
If Option B (adopt confirm-dialog for bulk-delete): touches
bulk_actions.html fragment + the _user_can_mutate delete flows.
Visual change — replaces browser confirm() with framework dialog.
Needs a11y review (focus trap, keyboard dismissal).
If Option C: deletes ~100 lines of template + ~40 lines of Alpine
JS + a contract file + regression tests. No user-visible change in
production (nothing was rendering these anyway). Devs who rely on
$dispatch('dz-confirm', …) in custom code would see silent no-ops
become errors.
Open questions¶
- Is there a reason these were kept dormant? The PR #600 commit message would clarify; if the primitives were explicitly deferred pending a future feature, Option C might be premature. If they were "write it and forget it," Option C is safe.
hx-confirmvs.confirm_dialogdesign decision — are there outstanding UX complaints about the browser's default confirm prompt? If yes, Option B (adopt confirm-dialog) has a product case. If no, Option A or C is more appropriate.- Dropdown vs. context-menu vs. popover overlap — the framework
has several menu-like primitives (dropdown, context_menu, popover,
alpine/dropdown). Why? Is one the "canonical" menu and the others
legacy? A
helper_audit-style cycle could disambiguate. - Automated lint for orphans — should the framework add a CI
check that flags templates with zero production
{% include %}consumers? Could live intests/unit/test_template_orphan_scan.pyfollowing the cycle 284 lint-rule pattern. Would prevent future dormant primitives from accumulating silently. - How many other PR #600 primitives are dormant? Cycle 287's
targeted scan only checked
components/alpine/*.html. A broader sweep acrosssrc/dazzle_page/runtime/static/js/dz-alpine.jscould find Alpine data components with zerox-data="…"consumers — the same class of dead-interface thatdzConfirmexemplifies. - Cost of keeping vs. deleting — keeping dormant primitives costs: test execution time (15+15 dormant tests = minor), reading burden on future devs (non-zero), governance overhead (contracts to maintain). Deleting costs: one-time destruction of work, potential need to recreate. Mostly a judgment call.
Recommendation¶
No unilateral action this cycle. The decision between A/B/C is a product-direction call, not a framework-hygiene call. Flagging for user input:
- If the user wants to preserve optionality: Option A (annotation only).
- If the user wants to actually use these primitives: Option B (pick one or both, spec the first adopter, spawn a cycle per adoption).
- If the user wants to shed weight: Option C (deletion sweep).
Cycle 286 flagged the meta-question for dropdown with a 3-6 cycle
wait. Cycle 287 extends the question to confirm_dialog. Next user
message mentioning these primitives should nudge toward a decision.
Status as of cycle 329 (2026-04-20)¶
Re-verified after ~40 cycles of dormancy. Still zero production consumers across all 4 primitives:
components/alpine/confirm_dialog.html → 0 consumers
components/alpine/dropdown.html → 0 consumers
components/modal.html → 0 consumers
components/island.html → 0 consumers
Cycles 286 + 287 flagged the decision as "awaiting user input." The no-decision state has persisted across: - PR #600 itself (cycles 286, 287 original discovery) - Cycle 302's orphan_lint scanner (surfaced all 4 automatically) - Cycle 304's scanner hardening (added dropdown after false negative) - Cycle 322's allowlist audit (11/11 entries VALID — including these 4) - Cycle 328's Python-module orphan rule-out (scope-setting for future)
Policy stance (cycle 329, re-affirmed):
Option A (accept + document) — the current default. Keep the dormant primitives, keep their ux-architect contracts, keep the orphan_lint allowlist entries citing PR #600 + cycle 286/287. No action. Re-evaluate at v1.0 (when API-stability commitments tighten the cost of deletion vs. preservation).
Why the re-affirmation is worth noting: - The automatic lint (cycle 302) has turned passive documentation into active accountability. Each test run surfaces the allowlist; each cycle reads the reason. The primitives are no longer "forgotten code" — they're explicitly tracked-dormant. - The ux-architect contracts for each primitive serve as documentation/intent even without adoption. Deletion would lose that intent capture. - Zero user-facing impact. Zero ongoing maintenance cost beyond the allowlist entry (~1 line per primitive).
When to revisit: - User explicitly asks about adopting, deleting, or repurposing any of the 4 primitives - v1.0 API-stability milestone - An example app's DSL introduces a component whose functionality matches one of the primitives (would trigger an "adopt instead of reinvent" judgement)
"Dormant primitives review" candidate — removed from next-cycle queue in favor of Option A as the durable answer. The candidate will return naturally if any of the re-visit triggers above fire.